Re: Factor https://re.factorcode.org/index.html Recent content on Re: Factor Hugo -- gohugo.io en-us Wed, 04 Feb 2026 08:00:00 -0700 Standard Deviation https://re.factorcode.org/2026/02/standard-deviation.html Wed, 04 Feb 2026 08:00:00 -0700 https://re.factorcode.org/2026/02/standard-deviation.html <p><a href="https://en.wikipedia.org/wiki/Standard_deviation">Standard deviation</a> is &ldquo;<em>a measure of the amount of variation of the values of a variable about its mean.</em>&rdquo;. It&rsquo;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 &ndash; um, are they still called tweets? &ndash; 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>&mdash; 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">&#34;/usr/share/dict/words&#34;</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">&#34;%s: %s\n&#34;</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&rsquo;s pretty close and also pretty obvious we are using slightly different dictionaries. I&rsquo;m not sure what <em>deedeed</em> or <em>poroporo</em> mean and they aren&rsquo;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 -0700 https://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&amp;qid=1671730412&amp;sprefix=physically+based%2Caps%2C145&amp;sr=8-1&amp;linkCode=ll1&amp;tag=pharr-20&amp;linkId=81a816d90f0c7e872617f1f930a51fd6&amp;language=en_US&amp;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&rsquo;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 &#34;perspective&#34; &#34;float fov&#34; 45 Sampler &#34;halton&#34; &#34;integer pixelsamples&#34; 128 Integrator &#34;volpath&#34; Film &#34;rgb&#34; &#34;string filename&#34; &#34;simple.png&#34; &#34;integer xresolution&#34; [400] &#34;integer yresolution&#34; [400] WorldBegin # uniform blue-ish illumination from all directions LightSource &#34;infinite&#34; &#34;rgb L&#34; [ .4 .45 .5 ] # approximate the sun LightSource &#34;distant&#34; &#34;point3 from&#34; [ -30 40 100 ] &#34;blackbody L&#34; 3000 &#34;float scale&#34; 1.5 AttributeBegin Material &#34;dielectric&#34; Shape &#34;sphere&#34; &#34;float radius&#34; 1 AttributeEnd AttributeBegin Texture &#34;checks&#34; &#34;spectrum&#34; &#34;checkerboard&#34; &#34;float uscale&#34; [16] &#34;float vscale&#34; [16] &#34;rgb tex1&#34; [.1 .1 .1] &#34;rgb tex2&#34; [.8 .8 .8] Material &#34;diffuse&#34; &#34;texture reflectance&#34; &#34;checks&#34; Translate 0 0 -1 Shape &#34;bilinearmesh&#34; &#34;point3 P&#34; [ -20 -20 0 20 -20 0 -20 20 0 20 20 0 ] &#34;point2 uv&#34; [ 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">&#34;perspective&#34;</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">&#34;float&#34;</span> } </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;fov&#34;</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">&#34;halton&#34;</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">&#34;integer&#34;</span> } </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;pixelsamples&#34;</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">&#34;volpath&#34;</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">&#34;rgb&#34;</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">&#34;string&#34;</span> } </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;filename&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="s">&#34;simple.png&#34;</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">&#34;integer&#34;</span> } </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;xresolution&#34;</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">&#34;integer&#34;</span> } </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;yresolution&#34;</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">&#34;infinite&#34;</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">&#34;rgb&#34;</span> } </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;L&#34;</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">&#34;distant&#34;</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">&#34;point3&#34;</span> } </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;from&#34;</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">&#34;blackbody&#34;</span> } </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;L&#34;</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">&#34;float&#34;</span> } </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;scale&#34;</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">&#34;dielectric&#34;</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">&#34;sphere&#34;</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">&#34;float&#34;</span> } </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;radius&#34;</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">&#34;checks&#34;</span> } </span></span><span class="line"><span class="cl"> { value-type <span class="s">&#34;spectrum&#34;</span> } </span></span><span class="line"><span class="cl"> { class <span class="s">&#34;checkerboard&#34;</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">&#34;float&#34;</span> } </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;uscale&#34;</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">&#34;float&#34;</span> } </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;vscale&#34;</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">&#34;rgb&#34;</span> } </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;tex1&#34;</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">&#34;rgb&#34;</span> } </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;tex2&#34;</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">&#34;diffuse&#34;</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">&#34;texture&#34;</span> } </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;reflectance&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="nb">values </span>{ <span class="s">&#34;checks&#34;</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">&#34;bilinearmesh&#34;</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">&#34;point3&#34;</span> } </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;P&#34;</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">&#34;point2&#34;</span> } </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;uv&#34;</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 -0700 https://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 &ldquo;<em>replacement for the X11 window system protocol and architecture with the aim to be easier to develop, extend, and maintain</em>&rdquo;. 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 &ndash; yeah it sure does! Or rather &ndash; 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? ] [ &#34;ui.backend.cocoa&#34; ] } </span></span><span class="line"><span class="cl"> { [ os windows? ] [ &#34;ui.backend.windows&#34; ] } </span></span><span class="line"><span class="cl"><span class="gd">- { [ os unix? ] [ &#34;ui.backend.gtk3&#34; ] } </span></span></span><span class="line"><span class="cl"><span class="gi">+ { [ os unix? ] [ &#34;ui.backend.gtk2&#34; ] } </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"> &lt;&lt; { </span></span><span class="line"><span class="cl"> { [ os windows? ] [ &#34;opengl.gl.windows&#34; ] } </span></span><span class="line"><span class="cl"> { [ os macos? ] [ &#34;opengl.gl.macos&#34; ] } </span></span><span class="line"><span class="cl"><span class="gd">- { [ os unix? ] [ &#34;opengl.gl.gtk3&#34; ] } </span></span></span><span class="line"><span class="cl"><span class="gi">+ { [ os unix? ] [ &#34;opengl.gl.gtk2&#34; ] } </span></span></span><span class="line"><span class="cl"> [ unknown-gl-platform ] </span></span><span class="line"><span class="cl"> } cond use-vocab &gt;&gt; </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 -0700 https://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">&#34;google.com&#34;</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>&ldquo;millions of DNS records; of those just 743 are LOCs.&rdquo;</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&rsquo; 26.00&quot; N)</li> <li>longitude (122° 1&rsquo; 47.00&quot; 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">&#34;alink.net&#34;</span> dns-LOC-query answer-section&gt;&gt; ... </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">&#34;alink.net&#34;</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>&gt;&gt;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>&gt;&gt;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>&gt;&gt;vertical </span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>be&gt; <span class="m">31 </span><span class="nb">2^ - </span><span class="m">3600000 </span><span class="nb">/ </span>&gt;&gt;lat </span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>be&gt; <span class="m">31 </span><span class="nb">2^ - </span><span class="m">3600000 </span><span class="nb">/ </span>&gt;&gt;lon </span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>be&gt; <span class="m">10000000 </span><span class="nb">- </span>&gt;&gt;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&gt;&gt; [ </span></span><span class="line"><span class="cl"> rdata&gt;&gt; { </span></span><span class="line"><span class="cl"> [ lat&gt;&gt; [ <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">&#34;S&#34;</span> <span class="s">&#34;N&#34;</span> <span class="nb">? </span>] <span class="nb">bi </span>] </span></span><span class="line"><span class="cl"> [ lon&gt;&gt; [ <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">&#34;W&#34;</span> <span class="s">&#34;E&#34;</span> <span class="nb">? </span>] <span class="nb">bi </span>] </span></span><span class="line"><span class="cl"> [ alt&gt;&gt; <span class="m">100 </span><span class="nb">/ </span>] </span></span><span class="line"><span class="cl"> [ size&gt;&gt; <span class="m">100 </span><span class="nb">/i </span>] </span></span><span class="line"><span class="cl"> [ horizontal&gt;&gt; <span class="m">100 </span><span class="nb">/i </span>] </span></span><span class="line"><span class="cl"> [ vertical&gt;&gt; <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">&#34;%d %d %.3f %s %d %d %.3f %s %.2fm %dm %dm %dm\n&#34;</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">&#34;alink.net&#34;</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 -0700 https://re.factorcode.org/2025/12/factor-0-101-now-available.html <p><em>&ldquo;Keep thy airspeed up, lest the earth come from below and smite thee.&rdquo; - William Kershner</em></p> <p>I&rsquo;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&rsquo;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>&quot;&quot;</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 &quot;post-data&quot; 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&gt;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>&amp;[</code>, and short-circuiting <code>n&amp;&amp;[</code>, <code>n||[</code>, <code>&amp;&amp;[</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 &ldquo;live coding&rdquo; 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&gt;</code> and <code>&gt;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&lt;</code>, <code>year&lt;=</code>, <code>year&gt;</code>, <code>year&gt;=</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&gt;slots</code>, rename <code>tuple&gt;array</code> to <code>pack-tuple</code> and <code>&gt;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>&lt;font&gt;</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&rsquo;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&gt;file</code>, <code>file&gt;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 &ldquo;closest to zero&rdquo; 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>&quot;set4&quot;</code>, <code>&quot;set5&quot;</code>, and <code>&quot;set6&quot;</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>&lt;prefixed&gt;</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>&lt;cycles-from&gt;</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 &ldquo;Tahoe&rdquo; 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>&gt;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>&lt;WATCH ... WATCH&gt;</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 -0700 https://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 &ndash; 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&rsquo;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&rsquo;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 &ndash; 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">&#34;F@ct0r!&#34;</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&#39;t help very much. </span></span><span class="line"><span class="cl"> Predictable substitutions <span class="nb">like </span>&#39;@&#39; instead <span class="nb">of </span>&#39;a&#39; don&#39;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">&#34;john2025&#34;</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&rsquo;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 -0700 https://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&rsquo;s go over the benchmark in a few programming languages &ndash; 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">-&gt;</span> <span class="nb">float</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34;Time creating and destroying tasks.&#34;&#34;&#34;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34;Do nothing task.&#34;&#34;&#34;</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">&#34;</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&#34;</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"> &lt;iota&gt; [ ] 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>&lt;range&gt; [ </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">&#34;%7d tasks \t %7d tasks per/s\n&#34;</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&rsquo;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">&#34;fmt&#34;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="s">&#34;sync&#34;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="s">&#34;time&#34;</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">&amp;</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">&lt;</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">&lt;=</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">&#34;%7d tasks \t %7d tasks per/s\n&#34;</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">&lt;</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> &ndash; 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> &ndash; 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&rsquo;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 -0700 https://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">&gt;integer </span></span></span><span class="line"><span class="cl"> { n <span class="s">&#34;Fizz&#34;</span> <span class="s">&#34;Buzz&#34;</span> <span class="s">&#34;FizzBuzz&#34;</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">&#34;Fizz&#34;</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">&#34;Buzz&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;Fizz&#34;</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">&#34;Fizz&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;Buzz&#34;</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">&#34;Fizz&#34;</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">&#34;FizzBuzz&#34;</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">&#34;Fizz&#34;</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">&#34;Buzz&#34;</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">&#34;Fizz&#34;</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">&#34;Buzz&#34;</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">&#34;FizzBuzz&#34;</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 -0700 https://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&rsquo;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>&quot;Fizz&quot;</code> instead of the number;</li> <li>for multiples of five, print <code>&quot;Buzz&quot;</code> instead of the number;</li> <li>for multiples of both three and five, print <code>&quot;FizzBuzz&quot;</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&rsquo;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>&quot;Fizz&quot;</code> and a cycle of <code>&quot;Buzz&quot;</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>} &lt;circular&gt; <span class="m">10 </span>ltake list&gt;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">&#34;&#34;</span> <span class="s">&#34;&#34;</span> <span class="s">&#34;Fizz&#34;</span> } &lt;circular&gt; </span></span><span class="line"><span class="cl"> { <span class="s">&#34;&#34;</span> <span class="s">&#34;&#34;</span> <span class="s">&#34;&#34;</span> <span class="s">&#34;&#34;</span> <span class="s">&#34;Buzz&#34;</span> } &lt;circular&gt; </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">&#34;%2d: %s\n&#34;</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">&#34;Fizz&#34;</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">&#34;Buzz&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;Fizz&#34;</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">&#34;Fizz&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;Buzz&#34;</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">&#34;Fizz&#34;</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">&#34;FizzBuzz&#34;</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">&#34;Fizz&#34;</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">&#34;Buzz&#34;</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 -0700 https://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">&#34; &#34;</span> <span class="nb">join </span></span></span><span class="line"><span class="cl"> ] <span class="nb">replicate </span><span class="s">&#34;, &#34;</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&gt;upper ] <span class="nb">change-nth </span><span class="s">&#34;?.&#34;</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">&#34; &#34;</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">&#34;\ </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.&#34;</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"> &lt;iota&gt; [ </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">&#34;\n&#34;</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&rsquo;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>:&gt; 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">&#34;--w&#34;</span> } </span></span><span class="line"><span class="cl"> { help <span class="s">&#34;Generate some lorem ipsum words&#34;</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">&#34;--s&#34;</span> } </span></span><span class="line"><span class="cl"> { help <span class="s">&#34;Generate a lorem ipsum sentence&#34;</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">&#34;--p&#34;</span> } </span></span><span class="line"><span class="cl"> { help <span class="s">&#34;Generate a lorem ipsum paragraph&#34;</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">&#34;w&#34;</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">&#34;s&#34;</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">&#34;p&#34;</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 -0700 https://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 &mdash; 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&gt;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">&gt;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">&#34;N&#34;</span> } [ <span class="m">0 </span> compass&gt;string ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;NNE&#34;</span> } [ <span class="m">23.97 </span>compass&gt;string ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;NEbN&#34;</span> } [ <span class="m">33.7 </span> compass&gt;string ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;ENE&#34;</span> } [ <span class="m">73.12 </span>compass&gt;string ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;EbN&#34;</span> } [ <span class="m">73.13 </span>compass&gt;string ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;SWbS&#34;</span> } [ <span class="m">219 </span> compass&gt;string ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;W&#34;</span> } [ <span class="m">275 </span> compass&gt;string ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;WbN&#34;</span> } [ <span class="m">276 </span> compass&gt;string ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;WNW&#34;</span> } [ <span class="m">287 </span> compass&gt;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&gt;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 -0700 https://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&rsquo;re done.</li> <li>If the pixel is, then change it&rsquo;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&gt;&gt; <span class="nb">first2 </span>:&gt; <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">&gt;= </span>] [ x w <span class="nb">&lt; </span>] </span></span><span class="line"><span class="cl"> [ y <span class="m">0 </span><span class="nb">&gt;= </span>] [ y h <span class="nb">&lt; </span>] </span></span><span class="line"><span class="cl"> } 0&amp;&amp; [ </span></span><span class="line"><span class="cl"> x y image pixel-at :&gt; 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>:&gt; 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 } } :&gt; 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>:&gt; <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>:&gt; <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>:&gt; nx </span></span><span class="line"><span class="cl"> ty dy <span class="nb">+ </span>:&gt; 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">&gt;= </span>] [ nx w <span class="nb">&lt; </span>] </span></span><span class="line"><span class="cl"> [ ny <span class="m">0 </span><span class="nb">&gt;= </span>] [ ny h <span class="nb">&lt; </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&amp;&amp; [ </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&rsquo;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> &lt; <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">&lt;floodfill-gadget&gt;</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">&gt;integer </span>] <span class="nb">bi@ </span>:&gt; <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&gt;&gt; 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">&#34;gestures&#34;</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">&#34;Floodfill&#34;</span> } } </span></span><span class="line"><span class="cl"> <span class="s">&#34;vocab:floodfill/logo.png&#34;</span> &lt;floodfill-gadget&gt; &gt;&gt;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 -0700 https://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 &ldquo;<em>lightweight package for parsing chemical formula strings into python dictionaries</em>&rdquo; mapping chemical elements to numeric counts.</p> <p>It supports parsing several variants of formula such as:</p> <ul> <li>simple formulas like <code>&quot;H2O&quot;</code></li> <li>fractional stoichiometry like <code>&quot;C1.5O3&quot;</code></li> <li>groups such as <code>&quot;(CH3)2&quot;</code></li> <li>nested groups such as <code>&quot;((CH3)2)3&quot;</code></li> <li>square brackets such as <code>&quot;K4[Fe(SCN)6]&quot;</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]? =&gt; [[ <span class="nb">sift &gt;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">&#34;.&#34;</span> [0-9]+ }? { { <span class="s">&#34;e&#34;</span> | <span class="s">&#34;E&#34;</span> } { <span class="s">&#34;+&#34;</span> | <span class="s">&#34;-&#34;</span> }? [0-9]+ }? </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> =&gt; [[ <span class="nb">first3 </span>[ <span class="nb">concat </span>] <span class="nb">bi@ </span><span class="s">&#34;&#34;</span> <span class="nb">3append-as </span>string&gt;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">&#34;(&#34;</span>~ pair+ <span class="s">&#34;)&#34;</span>~ | <span class="s">&#34;[&#34;</span>~ pair+ <span class="s">&#34;]&#34;</span>~ } <span class="nb">number? </span></span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> =&gt; [[ <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">&#34;H2O&#34;</span> split-formula <span class="m">. </span></span></span><span class="line"><span class="cl">V{ { <span class="s">&#34;H&#34;</span> <span class="m">2 </span>} { <span class="s">&#34;O&#34;</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">&#34;(CH3)2&#34;</span> split-formula <span class="m">. </span></span></span><span class="line"><span class="cl">V{ { V{ { <span class="s">&#34;C&#34;</span> <span class="m">1 </span>} { <span class="s">&#34;H&#34;</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>] [ &#39;[ _ _ 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"> &#39;[ <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">&#34;H&#34;</span> <span class="m">2 </span>} { <span class="s">&#34;O&#34;</span> <span class="m">1 </span>} } } [ <span class="s">&#34;H2O&#34;</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">&#34;C&#34;</span> <span class="m">1.5 </span>} { <span class="s">&#34;O&#34;</span> <span class="m">3 </span>} } } [ <span class="s">&#34;C1.5O3&#34;</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">&#34;C&#34;</span> <span class="m">2 </span>} { <span class="s">&#34;H&#34;</span> <span class="m">6 </span>} } } [ <span class="s">&#34;(CH3)2&#34;</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">&#34;C&#34;</span> <span class="m">6 </span>} { <span class="s">&#34;H&#34;</span> <span class="m">18 </span>} } } [ <span class="s">&#34;((CH3)2)3&#34;</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">&#34;K&#34;</span> <span class="m">4 </span>} { <span class="s">&#34;Fe&#34;</span> <span class="m">1 </span>} { <span class="s">&#34;S&#34;</span> <span class="m">6 </span>} { <span class="s">&#34;C&#34;</span> <span class="m">6 </span>} { <span class="s">&#34;N&#34;</span> <span class="m">6 </span>} } } </span></span><span class="line"><span class="cl">[ <span class="s">&#34;K4[Fe(SCN)6]&#34;</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 -0700 https://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&rsquo;s splitlines does a lot more than just newlines</a>:</p> <blockquote> <p>I always assumed that Python&rsquo;s <a href="https://docs.python.org/3/library/stdtypes.html#str.splitlines">str.splitlines()</a> split strings by &ldquo;<a href="https://docs.python.org/3/glossary.html#term-universal-newlines">universal newlines</a>&rdquo;, 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">&#34;line1\nline2\rline3\r\nline4\vline5\x1dhello&#34;</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">&#34;line1&#34;</span> <span class="s">&#34;line2&#34;</span> <span class="s">&#34;line3&#34;</span> <span class="s">&#34;line4&#34;</span> <span class="s">&#34;line5&#34;</span> <span class="s">&#34;hello&#34;</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 &ndash; 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 -0700 https://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> &ndash; also known as EDN &ndash; 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">&#34;hungarian breakfast&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;farmer&#39;s cheesy omelette&#34;</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&#39;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">&#34;yum!&#34;</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&#39;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">&#34;tell the people what she wore&#34;</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">&#34;the more you see the more you hate&#34;</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">&#34;huat&#34;</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">&#34;eggs-benedict&#34;</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">&#34;hungarian breakfast&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;farmer&#39;s cheesy omelette&#34;</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">&#34;eggs&#34;</span> } } </span></span><span class="line"><span class="cl"> T{ keyword { name <span class="s">&#34;cheese&#34;</span> } } </span></span><span class="line"><span class="cl"> T{ keyword { name <span class="s">&#34;olives&#34;</span> } } </span></span><span class="line"><span class="cl"> T{ symbol { name <span class="s">&#34;spoon&#34;</span> } } </span></span><span class="line"><span class="cl"> T{ symbol { name <span class="s">&#34;kitchen/spoon&#34;</span> } } </span></span><span class="line"><span class="cl"> T{ symbol { name <span class="s">&#34;kitchen/fork&#34;</span> } } </span></span><span class="line"><span class="cl"> T{ symbol { name <span class="s">&#34;github/fork&#34;</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">&#34;bun&#34;</span> } } </span></span><span class="line"><span class="cl"> T{ keyword { name <span class="s">&#34;beef-patty&#34;</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">&#34;yum!&#34;</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">&#34;gelato&#34;</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">&#34;eggs&#34;</span> } } <span class="m">2 </span>} </span></span><span class="line"><span class="cl"> { T{ keyword { name <span class="s">&#34;lemon-juice&#34;</span> } } <span class="m">3.5 </span>} </span></span><span class="line"><span class="cl"> { T{ keyword { name <span class="s">&#34;butter&#34;</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">&#34;tell the people what she wore&#34;</span> } </span></span><span class="line"><span class="cl"> { V{ <span class="m">5 6 7 8 </span>} <span class="s">&#34;the more you see the more you hate&#34;</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">&#34;a&#34;</span> } } </span></span><span class="line"><span class="cl"> T{ keyword { name <span class="s">&#34;b&#34;</span> } } </span></span><span class="line"><span class="cl"> <span class="s">&#34;huat&#34;</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">&#34;MyYelpClone/MenuItem&#34;</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">&#34;name&#34;</span> } } <span class="s">&#34;eggs-benedict&#34;</span> } </span></span><span class="line"><span class="cl"> { T{ keyword { name <span class="s">&#34;rating&#34;</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">&#34;edn&#34;</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&gt; <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"> &gt;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&rsquo;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 -0700 https://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&hellip;)</p> </blockquote> <p>It&rsquo;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">&gt;&gt;</span><span class="w"> </span><span class="mi">16</span><span class="p">)</span><span class="w"> </span><span class="o">&amp;</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">&amp;</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">&lt;</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">&lt;&lt;</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&rsquo;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">&gt;integer bitxor </span><span class="m">32 </span>&gt;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>&gt;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">&#34;%3d %12d\n&#34;</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 &ndash; it matches!</p> std::flip https://re.factorcode.org/2025/09/std-flip.html Mon, 29 Sep 2025 20:00:00 -0700 https://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&rsquo;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) [ &#39;[ _ -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">&#34;a&#34;</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">&#34;a&#34;</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">&#34;a&#34;</span> <span class="s">&#34;b&#34;</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">&#34;b&#34;</span> <span class="s">&#34;a&#34;</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">&#34;a&#34;</span> <span class="s">&#34;b&#34;</span> <span class="s">&#34;c&#34;</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">&#34;c&#34;</span> <span class="s">&#34;b&#34;</span> <span class="s">&#34;a&#34;</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">&#34;a&#34;</span> <span class="s">&#34;b&#34;</span> <span class="s">&#34;c&#34;</span> <span class="s">&#34;d&#34;</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">&#34;d&#34;</span> <span class="s">&#34;c&#34;</span> <span class="s">&#34;b&#34;</span> <span class="s">&#34;a&#34;</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">&#34;a&#34;</span> <span class="s">&#34;b&#34;</span> <span class="s">&#34;c&#34;</span> <span class="s">&#34;d&#34;</span> <span class="s">&#34;e&#34;</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">&#34;e&#34;</span> <span class="s">&#34;d&#34;</span> <span class="s">&#34;c&#34;</span> <span class="s">&#34;b&#34;</span> <span class="s">&#34;a&#34;</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&gt;&gt; <span class="nb">length </span>] <span class="nb">keep </span></span></span><span class="line"><span class="cl"> &#39;[ _ 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 &ndash; 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&gt;&gt; <span class="nb">length </span>] <span class="nb">keep </span></span></span><span class="line"><span class="cl"> &#39;[ _ 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 -0700 https://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&rsquo;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&rsquo;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">&#34;ABCDEFGHIJKLMNOPQRSTUVWXYZ&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;AÁĂẮẶẰẲẴǍÂẤẬẦẨẪÄǞȦǠẠȀÀẢȂĀĄ&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">zip </span>&gt;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">&gt;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&gt;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&gt;</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">&#34;FACTOR!&#34;</span> &gt;scream <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;ẰAĂẠẪȦ!&#34;</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">&#34;ẰAĂẠẪȦ!&#34;</span> scream&gt; <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;FACTOR!&#34;</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 -0700 https://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">&#34;FACTOR&#34;</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">&#34;&#34;</span> <span class="s">&#34;FACTOR&#34;</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">&#34;FACTOR&#34;</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">&#34;FACTOR&#34;</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">&#34;&#34;</span> <span class="s">&#34;FACTOR&#34;</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">&#34;FACTOR&#34;</span> os-env <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;&#34;</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">&#34;FACTOR&#34;</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">&#34;FACTOR&#34;</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 -0700 https://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 &ldquo;small window&rdquo; 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 -0700 https://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 &ldquo;<em>not a rewrite but a continuation and extension of Vim</em>&rdquo;. 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&rsquo;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&rsquo;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&rsquo;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 -0700 https://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&rsquo;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> &ndash; 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> &ldquo;HiDPI&rdquo; 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 -0700 https://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 &ldquo;<em>technical analysis to your own financial market trading applications</em>&rdquo;. 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&hellip; <a href="https://ta-lib.org/functions/">See complete list&hellip;</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">&lt;&lt; <span class="s">&#34;ta-lib&#34;</span> { </span></span><span class="line"><span class="cl"> { [ os windows? ] [ <span class="s">&#34;libta-lib.dll&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ os macos? ] [ <span class="s">&#34;libta-lib.dylib&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ os unix? ] [ <span class="s">&#34;libta-lib.so&#34;</span> ] } </span></span><span class="line"><span class="cl">} <span class="nb">cond </span>cdecl add-library &gt;&gt; </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 &lt;ref&gt; :&gt; outbegidx </span></span><span class="line"><span class="cl"> <span class="m">0 </span>int &lt;ref&gt; :&gt; outnbelement </span></span><span class="line"><span class="cl"> <span class="nb">real </span>check-array :&gt; inreal </span></span><span class="line"><span class="cl"> inreal <span class="nb">length </span>:&gt; len </span></span><span class="line"><span class="cl"> inreal check-begidx1 :&gt; begidx </span></span><span class="line"><span class="cl"> len <span class="m">1 </span><span class="nb">- </span>begidx <span class="nb">- </span>:&gt; endidx </span></span><span class="line"><span class="cl"> timeperiod TA_RSI_Lookback begidx <span class="nb">+ </span>:&gt; lookback </span></span><span class="line"><span class="cl"> len lookback make-double-array :&gt; 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&rsquo;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&rsquo;t have enough data to compute an answer &ndash; 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 -0700 https://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>&quot;🤦🏼‍♂️&quot;.length == 7</code></p> <p>But It’s Better that <code>&quot;🤦🏼‍♂️&quot;.len() == 17</code> and Rather Useless that <code>len(&quot;🤦🏼‍♂️&quot;) == 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&rsquo;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">&#34;🤦&#34;</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">&#34;🤦🏼‍♂️&#34;</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">&#34;🤦🏼‍♂️&#34;</span> [ char&gt;name <span class="m">. </span>] <span class="nb">each </span></span></span><span class="line"><span class="cl"><span class="s">&#34;face-palm&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;emoji-modifier-fitzpatrick-type-3&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;zero-width-joiner&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;male-sign&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;variation-selector-16&#34;</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">&#34;🤦🏼‍♂️&#34;</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">&#34;🤦🏼‍♂️&#34;</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">&#34;🤦🏼‍♂️&#34;</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">&#34;🤦🏼‍♂️&#34;</span> &gt;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 -0700 https://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&hellip; 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 &ldquo;<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>&rdquo;. 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 &mdash; 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 &mdash; 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 &#34;5d737f0600ff2dd%d&#34; 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>&lt;iota&gt; [ </span></span><span class="line"><span class="cl"> &gt;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">&#34;000043f7c4392a781a04419a7cb503089ebcf3164e2b1d4258b3e6c15b8b07f1&#34;</span> </span></span><span class="line"><span class="cl">} [ <span class="s">&#34;5d737f0600ff2dd&#34;</span> find-anubis bytes&gt;hex-string ] unit-test </span></span></code></pre></div><p>But, I noticed that it&rsquo;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">&#34;5d737f0600ff2dd&#34;</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>:&gt; 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>&lt;byte-array&gt; :&gt; hash </span></span><span class="line"><span class="cl"> <span class="m">18 </span><span class="nb">2^ </span>&lt;iota&gt; [ </span></span><span class="line"><span class="cl"> base <span class="nb">clone </span>:&gt; ctx </span></span><span class="line"><span class="cl"> ctx <span class="nb">swap </span>&gt;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">&#34;5d737f0600ff2dd&#34;</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>&lt;iota&gt; [ </span></span><span class="line"><span class="cl"> [ <span class="nb">clone </span>] <span class="nb">dip </span>&gt;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">&#34;5d737f0600ff2dd&#34;</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&rsquo;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">&#34;5d737f0600ff2dd&#34;</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">&#34;Initial&#34;</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&gt; </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">&gt; </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&gt;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 &gt;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&gt; </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&gt;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&gt;&gt;</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&gt;&gt;</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&gt;&gt;</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> &gt;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&gt;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> &lt;byte-array&gt; </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&gt;&gt;</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> &gt;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">&lt; </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&gt;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> &gt;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&gt;&gt;</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&lt;&lt;</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> &gt;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&gt;&gt;</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">&gt; </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">&gt; </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&gt;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">&lt;string&gt; </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 &gt;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 -0700 https://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&rsquo;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&rsquo;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">&gt;=</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">&lt;=</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">&gt;</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">&lt;</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">=&gt;</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">=&gt;</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">&gt;=</span> <span class="mi">1</span> <span class="o">&amp;&amp;</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">&lt;=</span> <span class="mi">3</span><span class="p">)</span> <span class="o">&amp;&amp;</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">=&gt;</span> <span class="nx">x</span> <span class="o">&gt;</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">=&gt;</span> <span class="nx">x</span> <span class="o">&lt;</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&rsquo;s nothing quite like syntax wars &ndash; the nerd version of the <a href="https://en.wikipedia.org/wiki/Linguistics_wars">linguistic wars</a> &ndash; 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&rsquo;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&rsquo;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">&lt;=</span> <span class="nb">abs</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">&lt;=</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">&gt;</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">&lt;</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">&gt;=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&amp;&amp;</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">&lt;=</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&amp;&amp;</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">&gt;</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">&lt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">&gt;=</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&amp;</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">&lt;=</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">&gt;</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">&lt;</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">-&gt;</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">&gt;</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">&lt;</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">&lt;=</span> <span class="nb">abs</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">&lt;=</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">&gt;</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">&lt;</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">&gt; </span>] <span class="nb">all? </span></span></span><span class="line"><span class="cl"> line [ <span class="m">0 </span><span class="nb">&lt; </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">&gt; </span>] <span class="nb">all? </span></span></span><span class="line"><span class="cl"> line [ <span class="m">0 </span><span class="nb">&lt; </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">&gt; </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">&lt; </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">&gt; </span>] <span class="nb">all? </span>] [ [ <span class="m">0 </span><span class="nb">&lt; </span>] <span class="nb">all? </span>] } 1|| ] </span></span><span class="line"><span class="cl"> } 1&amp;&amp; </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>&#39;[ <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&amp;&amp; [ </span></span><span class="line"><span class="cl"> &#39;[ { [ <span class="nb">abs </span><span class="m">1 3 </span>between? ] [ <span class="nb">sgn </span>] } 1&amp;&amp; _ <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>[ &#39;[ 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&amp;&amp; ] 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>[ &#39;[ _ _ 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"> [ &#39;[ _ 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&rsquo;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 -0700 https://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&rsquo;s a good start!</p> <p>I thought I&rsquo;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&gt;</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&gt;</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&gt;</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&gt;</span> binary [ unpickle ] with-byte-reader <span class="k">; </span></span></span></code></pre></div><p>In addition, we needed to support Python&rsquo;s string escapes which are slightly different than the ones that Factor defines &ndash; 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&rsquo;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&rsquo;t be too hard to add.</p> <p>But, despite that, it works pretty well!</p> <p>Here&rsquo;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">&gt;&gt;&gt;</span> <span class="n">data</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;abc&#34;</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">&#34;a&#34;</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">&gt;&gt;&gt;</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">&gt;&gt;&gt;</span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">&#39;pickles&#39;</span><span class="p">,</span> <span class="s1">&#39;wb&#39;</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">&#34;pickles&#34;</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">&#34;pickles&#34;</span> binary file-contents pickle&gt; <span class="m">. </span></span></span><span class="line"><span class="cl">V{ <span class="s">&#34;abc&#34;</span> <span class="m">123 4.56 </span>H{ { <span class="s">&#34;a&#34;</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 -0700 https://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 &ldquo;<em>create beautiful slide decks using an intuitive Markdown experience</em>&rdquo;.</p> <p>If you&rsquo;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&rsquo;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">&#34;Quotations&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Quotation: un-named blocks of code&#34;</span> </span></span><span class="line"><span class="cl"> { $code <span class="s">&#34;[ \&#34;Hello, World\&#34; print ]&#34;</span> } </span></span><span class="line"><span class="cl"> <span class="s">&#34;Combinators: words taking quotations&#34;</span> </span></span><span class="line"><span class="cl"> { $code <span class="s">&#34;10 dup 0 &lt; [ 1 - ] [ 1 + ] if .&#34;</span> } </span></span><span class="line"><span class="cl"> { $code <span class="s">&#34;{ -1 1 -2 0 3 } [ 0 max ] map .&#34;</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 [ &#34;Hello, World&#34; print ] ``` - Combinators: words taking quotations ```factor 10 dup 0 &lt; [ 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">&#34;--- </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: &#39;SF Pro Display&#39;, &#39;Segoe UI&#39;, 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: \&#34;▸ \&#34;; </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"> }&#34;</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">&#34;~/FACTOR.md&#34;</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 =&gt; 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 -0700 https://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> &ndash; <em>It&rsquo;s like JSON, but fast and small</em></li> <li><a href="https://toml.io/en/">TOML</a> or <em>Tom&rsquo;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 &ndash; for example <code>csv</code> doesn&rsquo;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">&#34;name&#34;</span> <span class="s">&#34;Factor&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;age&#34;</span> <span class="m">22 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;list&#34;</span> { <span class="m">4 8 15 16 23 42 </span>} } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;map&#34;</span> { LH{ { <span class="s">&#34;one&#34;</span> <span class="m">1 </span>} { <span class="s">&#34;two&#34;</span> <span class="m">2 </span>} } } } </span></span><span class="line"><span class="cl"> } [ </span></span><span class="line"><span class="cl"> &gt;json json&gt; </span></span><span class="line"><span class="cl"> &gt;msgpack msgpack&gt; </span></span><span class="line"><span class="cl"> &gt;toml toml&gt; </span></span><span class="line"><span class="cl"> &gt;cbor cbor&gt; </span></span><span class="line"><span class="cl"> &gt;bson bson&gt; </span></span><span class="line"><span class="cl"> &gt;bencode bencode&gt; </span></span><span class="line"><span class="cl"> &gt;yaml yaml&gt; </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&gt;bytes bytes&gt;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&rsquo;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&rsquo;s welcome!</p> Tribonacci Numbers https://re.factorcode.org/2025/07/tribonacci-numbers.html Thu, 24 Jul 2025 08:00:00 -0700 https://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> &mdash; in this case: <code>a=0</code>, <code>b=1</code>, <code>c=1</code>.</em></p> <p>Let&rsquo;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">&#34;%s = %s\n&#34;</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>} :&gt; 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">&gt; </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>&lt;iota&gt; [ 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 -0700 https://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">-&gt;</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">&lt;</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">&lt; </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 &gt; </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 &gt; </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 &gt; </span>[ </span></span><span class="line"><span class="cl"> [ ] [ <span class="nb">rot </span>] [ <span class="nb">-rot </span>] [ &#39;[ @ [ <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">&gt; </span>] [ </span></span><span class="line"><span class="cl"> x y :&gt; <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">&lt;= </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">&gt;= </span>] [ y ] } </span></span><span class="line"><span class="cl"> { [ y z <span class="nb">&lt;= </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">&gt;= </span>[ y ] [ y z <span class="nb">&lt;= </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 -0700 https://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">&#39;Yes or no, please!&#39;</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">&#39;y&#39;</span><span class="p">,</span> <span class="s1">&#39;ye&#39;</span><span class="p">,</span> <span class="s1">&#39;yes&#39;</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">&#39;n&#39;</span><span class="p">,</span> <span class="s1">&#39;no&#39;</span><span class="p">,</span> <span class="s1">&#39;nop&#39;</span><span class="p">,</span> <span class="s1">&#39;nope&#39;</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">&lt;</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">&#39;invalid user response&#39;</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>&gt;&gt;&gt; ask_ok(&#34;Continue? &#34;) Continue? y True &gt;&gt;&gt; ask_ok(&#34;Continue? &#34;) Continue? no False &gt;&gt;&gt; ask_ok(&#34;Continue? &#34;) 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 &#34;&lt;python-input-1&gt;&#34;, line 1, in &lt;module&gt; ask_ok(&#34;Continue? &#34;) ~~~~~~^^^^^^^^^^^^^^ File &#34;&lt;python-input-0&gt;&#34;, line 10, in ask_ok raise IOError(&#39;invalid user response&#39;) 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">&#34;y&#34;</span> <span class="s">&#34;ye&#34;</span> <span class="s">&#34;yes&#34;</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">&#34;n&#34;</span> <span class="s">&#34;no&#34;</span> <span class="s">&#34;nop&#34;</span> <span class="s">&#34;nope&#34;</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">&lt; </span>[ <span class="s">&#34;invalid user response&#34;</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">&#34;Continue?&#34;</span> <span class="m">4 </span><span class="s">&#34;Yes or no, please!&#34;</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">&#34;Continue?&#34;</span> <span class="m">4 </span><span class="s">&#34;Yes or no, please!&#34;</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">&#34;Continue?&#34;</span> <span class="m">4 </span><span class="s">&#34;Yes or no, please!&#34;</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">&#34;Yes or no, please!&#34;</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">&#34;invalid user response&#34;</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">&#34;yes&#34;</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">&#34;nope&#34;</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">&lt;ask&gt;</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">&#34;Yes or no, please!&#34;</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&gt;&gt; <span class="nb">write bl flush readln </span>{ </span></span><span class="line"><span class="cl"> { [ <span class="s">&#34;yes&#34;</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">&#34;nope&#34;</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">&lt; </span>[ <span class="s">&#34;invalid user response&#34;</span> <span class="nb">throw </span>] <span class="nb">when </span></span></span><span class="line"><span class="cl"> ask complaint&gt;&gt; <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">&#34;Continue?&#34;</span> &lt;ask&gt; 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>&#39;[ </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">&#34;yes&#34;</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">&#34;nope&#34;</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">&#34;Continue?&#34;</span> ask-ok ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Yes or no, please?&#34;</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">&#34;r&#34;</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">&#34;yes&#34;</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">&#34;nope&#34;</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">&#34;invalid user response&#34;</span> </span></span><span class="line"><span class="cl"> { { <span class="s">&#34;Yes&#34;</span> <span class="no">t </span>} { <span class="s">&#34;Nope&#34;</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">&#34;Continue?&#34;</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">&#34;yes&#34;</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">&#34;nope&#34;</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 &ndash; or make it more complex without impacting the calling context &ndash; 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 -0700 https://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 &ndash; 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&gt;&gt; replica checkpoint&gt;&gt; <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&gt;&gt; replica checkpoint&gt;&gt; <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&gt;&gt; ] [ checkpoint&gt;&gt; ] <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&gt;&gt; replica checkpoint&gt;&gt; <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>&#39;[ _ [ commit-min&gt;&gt; ] [ checkpoint&gt;&gt; ] <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 -0700 https://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>&mdash; Dungeons &amp; 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&lt;= <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&gt; <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">&#34;💩🤬🤡😰🤮☠️😱😭🤢🙃🙈🙉🙊👍👀🙂😀🤗😍🐲&#34;</span> </span></span><span class="line"><span class="cl"> &gt;graphemes [ <span class="nb">&gt;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">&#34;💩&#34;</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">&#34;💩&#34;</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 -0700 https://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 &lt;= r &lt; 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> &ndash; which was the <em>fastest solution</em> &ndash; 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&rsquo;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 :&gt; r </span></span><span class="line"><span class="cl"> <span class="m">10,000 0 </span><span class="nb">&lt;array&gt; </span>:&gt; 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 :&gt; r </span></span><span class="line"><span class="cl"> 10,000 0 &lt;array&gt; :&gt; 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 :&gt; r </span></span><span class="line"><span class="cl"> 10,000 0 &lt;array&gt; :&gt; 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 :&gt; r </span></span></span><span class="line"><span class="cl"><span class="gi">+ 10,000 random { fixnum } declare :&gt; r </span></span></span><span class="line"><span class="cl"> 10,000 0 &lt;array&gt; :&gt; 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 :&gt; r </span></span><span class="line"><span class="cl"><span class="gd">- 10,000 0 &lt;array&gt; :&gt; a </span></span></span><span class="line"><span class="cl"><span class="gi">+ 10,000 uint32_t &lt;c-array&gt; :&gt; 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 :&gt; r </span></span><span class="line"><span class="cl"> 10,000 uint32_t &lt;c-array&gt; :&gt; 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 &lt;iota&gt; [ u mod ] map-sum :&gt; 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 &ndash; it can be computed outside the loop. And once you do that, you&rsquo;ll notice that each element of the array gets the same value, and you don&rsquo;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&rsquo;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>&lt;iota&gt; [ 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&rsquo;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 -0700 https://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&rsquo;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&rsquo;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">&#34;add&#34;</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">&#34;a&#34;</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">&#34;subtract&#34;</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">&#34;s&#34;</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 &#39;multiply&#39; for option &#39;command&#39; </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 -0700 https://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&rsquo;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 &ldquo;short code&rdquo;. 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&rsquo;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 &ldquo;first&rdquo; 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">&lt; </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">&lt; </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>:&gt; f(n-1)! </span></span><span class="line"><span class="cl"> <span class="m">1 </span>:&gt; 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>:&gt; 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 &ndash; 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 -0700 https://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 &ndash; and <a href="https://rosettacode.org/wiki/Category:Solutions_by_Programming_Task">RosettaCode tasks</a> &ndash; 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&rsquo;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> :&gt; <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>[-] :&gt; delta </span></span><span class="line"><span class="cl"> len2 &lt;bit-array&gt; :&gt; 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 [-] :&gt; 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 :&gt; 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"> :&gt; <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&rsquo;m curious if anyone else has a simpler implementation &ndash; 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&rsquo;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 :&gt; jaro </span></span><span class="line"><span class="cl"> s1 s2 <span class="nb">min-length </span><span class="m">4 </span>min :&gt; 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 :&gt; #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">&#34;FARMVILLE&#34;</span> <span class="s">&#34;FAREMVIEL&#34;</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">&#34;My string&#34;</span> <span class="s">&#34;My tsring&#34;</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">&#34;My string&#34;</span> <span class="s">&#34;My ntrisg&#34;</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">&gt;</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">&gt;</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">&gt;</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">&gt;</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&rsquo;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 -0700 https://re.factorcode.org/2025/06/best-shuffle.html <p>The &ldquo;<a href="https://rosettacode.org/wiki/Best_shuffle">Best shuffle</a>&rdquo; 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> str <span class="nb">clone </span>:&gt; new-str </span></span><span class="line"><span class="cl"> str <span class="nb">length </span>:&gt; n </span></span><span class="line"><span class="cl"> n &lt;iota&gt; <span class="nb">&gt;array </span>randomize :&gt; range1 </span></span><span class="line"><span class="cl"> n &lt;iota&gt; <span class="nb">&gt;array </span>randomize :&gt; 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">&#34;%s, %s, (%d)\n&#34;</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">&#34;abracadabra&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;seesaw&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;elk&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;grrrrrr&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;up&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;a&#34;</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 &ndash; and happens to be <a href="https://www.tumblr.com/accidentallyquadratic">accidentally quadratic</a> &ndash; 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 -0700 https://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=&#34;Multiple\nLines&#34; NON_INTERPOLATED=&#39;raw text without variable interpolation&#39; 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 &ndash; 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">&#34; \t&#34;</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">&#34;#&#34;</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">&#34;\n&#34;</span> token <span class="s">&#34;\r\n&#34;</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">&#34;&#34;</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">&#34;\\&#34;</span> token hide [ <span class="s">&#34;\\&#39;&#34;</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: &#39; </span><span class="nb">= not </span>] satisfy 2choice repeat0 <span class="s">&#34;&#39;&#34;</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">&#34;\\&#34;</span> token hide [ <span class="s">&#34;\\`&#34;</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">&#34;`&#34;</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">&#34;\\&#34;</span> token hide [ <span class="s">&#34;\&#34;\\befnrt&#34;</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: &#34; </span><span class="nb">= not </span>] satisfy 2choice repeat0 <span class="s">&#34;\&#34;&#34;</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">&#34; \t\r\n&#34;</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> -&gt; value of VAR</li> </ul> <p>Default value</p> <ul> <li><code>${VAR:-default}</code> -&gt; value of <code>VAR</code> if set and non-empty, otherwise default</li> <li><code>${VAR-default}</code> -&gt; 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> -&gt; 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&#39;</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">&#34;$(&#34;</span> ?head [ </span></span><span class="line"><span class="cl"> <span class="s">&#34;)&#34;</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">&#34;${&#34;</span> ?head [ <span class="s">&#34;}&#34;</span> ?tail <span class="nb">drop </span>] [ <span class="s">&#34;$&#34;</span> ?head <span class="nb">drop </span>] <span class="nb">if </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;:-&#34;</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">&#34;-&#34;</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">&#34;&#34;</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">&#34;&#34;</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">&#34;=&#34;</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">&#34;.env&#34;</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=&#34;${HOST:-localhost}&#34; PORT=&#34;${PORT:-80}&#34; URL=&#34;https://${HOST}:${PORT}/index.html&#34; </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">&#34;URL&#34;</span> os-env <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;https://localhost:8080/index.html&#34;</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 -0700 https://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&rsquo;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&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe> </div> <p>Inspired by that &ndash; and a past effort at <a href="https://re.factorcode.org/2012/09/color-tab.html">color tab completion</a> &ndash; 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> &lt; <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">&lt;color-section&gt;</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>&gt;&gt;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">&#34; &#34;</span> &lt;label&gt; { <span class="m">5 0 </span>} &lt;border&gt; </span></span><span class="line"><span class="cl"> <span class="nb">swap </span>color&gt;&gt; &lt;solid&gt; &gt;&gt;interior </span></span><span class="line"><span class="cl"> COLOR: black &lt;solid&gt; &gt;&gt;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"> &lt;block </span></span><span class="line"><span class="cl"> [ call-next-method ] </span></span><span class="line"><span class="cl"> [ &lt;color-section&gt; add-section ] <span class="nb">bi </span></span></span><span class="line"><span class="cl"> block&gt; <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"> &lt;block </span></span><span class="line"><span class="cl"> [ <span class="no">\ COLOR:</span> pprint-word string&gt;&gt; text ] </span></span><span class="line"><span class="cl"> [ &lt;color-section&gt; add-section ] <span class="nb">bi </span></span></span><span class="line"><span class="cl"> block&gt; <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 &ndash; partly because I&rsquo;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 -0700 https://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&rsquo;re working with large Python objects and you want to be certain that you&rsquo;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">&lt;tracking-assoc&gt;</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&gt;&gt; <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&gt;&gt; <span class="nb">at* </span>] [ accessed-keys&gt;&gt; adjoin ] <span class="nb">2bi </span><span class="k">; </span></span></span></code></pre></div><p>And for fun &ndash; since we could have built a normal word to do this &ndash; 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&gt;&gt;</span> </span></span><span class="line"><span class="cl"> [ underlying&gt;&gt; <span class="nb">keys </span>] [ accessed-keys&gt;&gt; ] <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">&#34;name&#34;</span> <span class="s">&#34;John Doe&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;age&#34;</span> <span class="m">30 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;email&#34;</span> <span class="s">&#34;[email protected]&#34;</span> } </span></span><span class="line"><span class="cl">} &lt;tracking-assoc&gt; </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="s">&#34;name&#34;</span> <span class="nb">over at </span><span class="s">&#34;John Doe&#34;</span> <span class="nb">assert= </span></span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">[ accessed-keys&gt;&gt; <span class="s">&#34;Accessed keys: %u\n&#34;</span> printf ] </span></span><span class="line"><span class="cl">[ never-accessed-keys&gt;&gt; <span class="s">&#34;Never accessed keys: %u\n&#34;</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">&#34;name&#34;</span> } </span></span><span class="line"><span class="cl">Never accessed keys: HS{ <span class="s">&#34;email&#34;</span> <span class="s">&#34;age&#34;</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 -0700 https://re.factorcode.org/2025/06/bab-aaa-bb-bbb.html <p>Admittedly, I struggle sometimes when I read the word &ldquo;<a href="https://en.wikipedia.org/wiki/Monoid">monoid</a>&rdquo;. 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">&ldquo;A Monad is just a Monoid in the Category of Endofunctors&rdquo;</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&rsquo;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> &ndash; my first result in this space.</li> <li><a href="https://factorcode.org/slava/babaaabbbbb">⟨a, b | bab=aaa, bbb=bb⟩</a> &ndash; explore the equivalence class of a<sup>8</sup> in this remarkable monoid.</li> </ul> </blockquote> <p>He clarified in the discussion that &ldquo;<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>&rdquo;.</p> <p>The second link above &ndash; made extra fun because it uses <code>a=🍎</code> and <code>b=🍌</code> to make a more emojiful experience &ndash; 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 &ndash; or even possibly poetic &ndash; to do that exploration using <a href="https://factorcode.org">Factor</a>.</p> <p><em>Warning: Spoilers ahead!</em></p> <p>Let&rsquo;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">&#34;bab&#34;</span> <span class="s">&#34;aaa&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;bbb&#34;</span> <span class="s">&#34;bb&#34;</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 &ndash; since these are bi-directional and can be applied in either direction &ndash; 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 -&gt; b</code>. Notice that we&rsquo;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&rsquo;s often good practice to try each step out during development, so let&rsquo;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">&#34;aaaaaaaa&#34;</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">&#34;babaaaaa&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;ababaaaa&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;aababaaa&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;aaababaa&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;aaaababa&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;aaaaabab&#34;</span> </span></span><span class="line"><span class="cl">} </span></span></code></pre></div><p>The next state is nice to have, but we&rsquo;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&#39;</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">&#34;aaaaaaaa&#34;</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">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;babaaaaa&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;ababaaaa&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aababaaa&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aaababaa&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aaaababa&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aaaaabab&#34;</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">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;babaaaaa&#34;</span> <span class="s">&#34;aaaaaaaa&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;babaaaaa&#34;</span> <span class="s">&#34;babbabaa&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;babaaaaa&#34;</span> <span class="s">&#34;babababa&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;babaaaaa&#34;</span> <span class="s">&#34;babaabab&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;ababaaaa&#34;</span> <span class="s">&#34;aaaaaaaa&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;ababaaaa&#34;</span> <span class="s">&#34;ababbaba&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;ababaaaa&#34;</span> <span class="s">&#34;abababab&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aababaaa&#34;</span> <span class="s">&#34;aaaaaaaa&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aababaaa&#34;</span> <span class="s">&#34;aababbab&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aaababaa&#34;</span> <span class="s">&#34;aaaaaaaa&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aaababaa&#34;</span> <span class="s">&#34;babbabaa&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aaaababa&#34;</span> <span class="s">&#34;aaaaaaaa&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aaaababa&#34;</span> <span class="s">&#34;babababa&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aaaababa&#34;</span> <span class="s">&#34;ababbaba&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aaaaabab&#34;</span> <span class="s">&#34;aaaaaaaa&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aaaaabab&#34;</span> <span class="s">&#34;babaabab&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aaaaabab&#34;</span> <span class="s">&#34;abababab&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aaaaabab&#34;</span> <span class="s">&#34;aababbab&#34;</span> } </span></span><span class="line"><span class="cl">} </span></span></code></pre></div><p>Let&rsquo;s solve for the shortest paths, we keep track of states we&rsquo;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>:&gt; seen </span></span><span class="line"><span class="cl"> { { from } } :&gt; 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>&#39;[ <span class="nb">last length </span>_ <span class="nb">&gt; </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">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aaaaaaaaaa&#34;</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">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aaaaaaaaaa&#34;</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">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aaaaaaaaaa&#34;</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&rsquo;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>:&gt; 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>&#39;[ <span class="nb">last length </span>_ <span class="nb">&gt; </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">&#34;aaaaaaaa&#34;</span> <span class="s">&#34;aaaaaaaaaa&#34;</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&rsquo;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 -0700 https://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> &ndash; <em>have you implemented bitcask yet?</em> &ndash; 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 &ldquo;<em>log-structured hash-table for fast key/value data</em>&rdquo;, 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>&gt;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&gt;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&gt;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&rsquo;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&gt; <span class="m">4 </span><span class="nb">read </span>be&gt; [ </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&gt;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&gt;bytes <span class="nb">dup length </span><span class="m">4 </span>&gt;be <span class="nb">write write </span>] </span></span><span class="line"><span class="cl"> [ <span class="m">4 </span>&gt;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&gt; <span class="nb">read </span>bytes&gt;object <span class="m">4 </span><span class="nb">read </span>be&gt; </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&#39;</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&gt; <span class="m">4 </span><span class="nb">read </span>be&gt; [ <span class="nb">read </span>bytes&gt;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 &ndash; by appending to the <em>data file</em> &ndash; 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">&lt;bitcask&gt;</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">&#34;.idx&#34;</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&gt;&gt; <span class="s">&#34;.idx&#34;</span> <span class="nb">append </span>binary </span></span><span class="line"><span class="cl"> [ index&gt;&gt; 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&gt;&gt; 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&gt;&gt; <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&gt;&gt; <span class="nb">at* </span>[ </span></span><span class="line"><span class="cl"> bitcask path&gt;&gt; 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&gt;&gt; <span class="nb">key? </span>[ </span></span><span class="line"><span class="cl"> bitcask path&gt;&gt; binary [ </span></span><span class="line"><span class="cl"> key write-tombstone </span></span><span class="line"><span class="cl"> key bitcask index&gt;&gt; <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&gt;&gt; <span class="nb">assoc-size </span><span class="k">; </span></span></span></code></pre></div><p>It is helpful to implement <code>&gt;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">&gt;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&gt;&gt; binary [ </span></span><span class="line"><span class="cl"> bitcask index&gt;&gt; [ </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&gt;&gt; binary [ </span></span><span class="line"><span class="cl"> bitcask index&gt;&gt; </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 -0700 https://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&rsquo;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&rsquo;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> &ndash; something I&rsquo;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">&#34;game-of-life&#34;</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&rsquo;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">&lt;grid&gt;</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"> &#39;[ _ &lt;bit-array&gt; ] <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"> &#39;[ _ { <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">&#34;&#34;</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 &ndash; 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> &ndash; a <a href="https://en.wikipedia.org/wiki/Nibble">nibble</a> &ndash; 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 :&gt; <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 &lt;nibble-array&gt; ] <span class="nb">replicate </span>:&gt; 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>:&gt; <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 &ndash; 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 :&gt; 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>:&gt; 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&rsquo;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> &lt; <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">&lt;grid-gadget&gt;</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>&gt;&gt;grid </span></span><span class="line"><span class="cl"> <span class="m">20 </span>&gt;&gt;size </span></span><span class="line"><span class="cl"> <span class="nb">dup </span>&#39;[ _ <span class="nb">dup </span>grid&gt;&gt; next-step relayout-1 ] </span></span><span class="line"><span class="cl"> <span class="no">f </span>1/10 seconds &lt;timer&gt; &gt;&gt;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&gt;&gt; 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&gt;&gt; grid-dim <span class="nb">swap </span>] [ size&gt;&gt; &#39;[ _ <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 &ndash; for example, by using the mouse scroll wheel to zoom in or out &ndash; 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&gt;&gt; <span class="nb">first2 </span>:&gt; <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&gt;&gt; :&gt; size </span></span><span class="line"><span class="cl"> h w [ size <span class="nb">/i </span>] <span class="nb">bi@ </span>:&gt; <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&gt;&gt; :&gt; grid </span></span><span class="line"><span class="cl"> grid grid-dim :&gt; <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 &lt;grid&gt; :&gt; 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&lt;&lt; </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&gt;&gt; :&gt; size </span></span><span class="line"><span class="cl"> gadget grid&gt;&gt; [| 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&gt;&gt; :&gt; size </span></span><span class="line"><span class="cl"> gadget grid&gt;&gt; grid-dim :&gt; <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>:&gt; <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>:&gt; 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>:&gt; 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">&ldquo;visual REPL&rdquo;</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&rsquo;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&gt;&gt; :&gt; grid </span></span><span class="line"><span class="cl"> gadget size&gt;&gt; :&gt; size </span></span><span class="line"><span class="cl"> grid grid-dim :&gt; <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>:&gt; <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&gt;&gt; :&gt; grid </span></span><span class="line"><span class="cl"> gadget size&gt;&gt; :&gt; size </span></span><span class="line"><span class="cl"> grid grid-dim :&gt; <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>:&gt; <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">&gt; </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">&lt; </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>&quot;gestures&quot;</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">&#34;gestures&#34;</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&gt;&gt; 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&gt;&gt; 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&gt;&gt; [ 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&gt;&gt; [ [ <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&gt;&gt; :&gt; 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&gt;&gt; next-step relayout-1 <span class="k">; </span></span></span></code></pre></div><p>And then store these as the <code>&quot;toolbar&quot;</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">&#34;toolbar&#34;</span> <span class="no">f </span>{ </span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">&#34;1&#34;</span> } } com-play } </span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">&#34;2&#34;</span> } } com-stop } </span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">&#34;3&#34;</span> } } com-clear } </span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">&#34;4&#34;</span> } } com-random } </span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">&#34;5&#34;</span> } } com-glider } </span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">&#34;6&#34;</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> &lt; <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">&lt;life-gadget&gt;</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 &lt;grid-gadget&gt; </span></span><span class="line"><span class="cl"> [ &lt;toolbar&gt; 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&gt;&gt; <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">&#34;Game of Life&#34;</span> } } </span></span><span class="line"><span class="cl"> &lt;life-gadget&gt; &gt;&gt;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 -0700 https://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 &ndash; and we have an extensive <a href="https://docs.factorcode.org/content/article-vocab-index.html">standard library</a> &ndash; I thought I&rsquo;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 &ndash; but still quite commonly used &ndash; <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">&#34;127.0.0.1&#34;</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">&#34;::1&#34;</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">&#34;127.0.0.1&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;1.1.1.1&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;8.8.8.8&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;192.168.10.40&#34;</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">&#34;1.1.1.1&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;8.8.8.8&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;127.0.0.1&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;192.168.10.40&#34;</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">&#34;2620:0:1cfe:face:b00c::3&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;2001:4860:4860::8844&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;2620:0:ccc::2&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;::1&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;2001:4860:4860::8888&#34;</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">&#34;::1&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;2001:4860:4860::8844&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;2001:4860:4860::8888&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;2620:0:ccc::2&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;2620:0:1cfe:face:b00c::3&#34;</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">&lt;&lt;EOF &gt; 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">&#34;read-lines .&#34;</span> </span></span><span class="line"><span class="cl"><span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="s2">&#34;2620:0:1cfe:face:b00c::3&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s2">&#34;2001:4860:4860::8844&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s2">&#34;2620:0:ccc::2&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s2">&#34;::1&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s2">&#34;2001:4860:4860::8888&#34;</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">&#34;read-lines [ parse-ipv6 ] sort-by [ print ] each&#34;</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 -0700 https://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">&amp;</span> <span class="mi">3221352463</span><span class="p">)</span> <span class="o">&lt;=</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&rsquo;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>[ &#39;[ _ _ <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 &ndash; and <em>typical</em> &ndash; 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&rsquo;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"> &#39;[ _ 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&hellip;</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">&lt;= </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"> &#39;[ _ 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&hellip;</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&lt;= <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"> &#39;[ _ faster-leap-year? _ <span class="nb">assert= </span>] average-benchmark <span class="k">; </span></span></span></code></pre></div><p>It&rsquo;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&hellip;</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&lt;= <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"> &#39;[ _ 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 -0700 https://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 &ldquo;<em>simple and easy-to-use library to enjoy videogames programming</em>&rdquo;. 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&hellip; 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> &ndash; an extremely simple program in the spirit of achieving a <a href="https://rampantgames.com/blog/?p=7745">black triangle</a> moment &ndash; 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> &ndash; 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">&#34;raylib [core] example - basic window&#34;</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">&#34;Congrats! You created your first window!&#34;</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 &ndash; you&rsquo;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&rsquo;re wondering why the background isn&rsquo;t white, it&rsquo;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&rsquo;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 -0700 https://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 &ndash; like <code>dup</code>, <code>swap</code>, <code>rot</code> &ndash; we have had the <a href="https://docs.factorcode.org/content/vocab-shuffle.html">shuffle vocabulary</a> which provides some &ldquo;additional shuffle words&rdquo; 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 &ndash; 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">&lt;array&gt; dup </span>&gt;R &gt;R R&gt; <span class="m">3 </span>set-slot </span></span><span class="line"><span class="cl"> R&gt; &gt;R R&gt; <span class="nb">dup </span>&gt;R &gt;R R&gt; <span class="m">2 </span>set-slot </span></span><span class="line"><span class="cl"> R&gt; &gt;R R&gt; <span class="nb">dup </span>&gt;R &gt;R R&gt; &gt;R R&gt; <span class="m">3 </span>slot R&gt; &gt;R R&gt; &gt;R R&gt; <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">&lt;array&gt; dup </span>&gt;R &gt;R R&gt; <span class="m">6 </span>set-slot </span></span><span class="line"><span class="cl"> R&gt; &gt;R R&gt; <span class="nb">dup </span>&gt;R &gt;R R&gt; <span class="m">5 </span>set-slot </span></span><span class="line"><span class="cl"> R&gt; &gt;R R&gt; <span class="nb">dup </span>&gt;R &gt;R R&gt; <span class="m">4 </span>set-slot </span></span><span class="line"><span class="cl"> R&gt; &gt;R R&gt; <span class="nb">dup </span>&gt;R &gt;R R&gt; <span class="m">3 </span>set-slot </span></span><span class="line"><span class="cl"> R&gt; &gt;R R&gt; <span class="nb">dup </span>&gt;R &gt;R R&gt; <span class="m">2 </span>set-slot </span></span><span class="line"><span class="cl"> R&gt; &gt;R R&gt; <span class="nb">dup </span>&gt;R &gt;R R&gt; &gt;R R&gt; <span class="m">3 </span>slot </span></span><span class="line"><span class="cl"> R&gt; <span class="nb">dup </span>&gt;R &gt;R R&gt; &gt;R R&gt; <span class="m">2 </span>slot R&gt; <span class="nb">dup </span>&gt;R &gt;R R&gt; &gt;R R&gt; <span class="m">5 </span>slot </span></span><span class="line"><span class="cl"> R&gt; <span class="nb">dup </span>&gt;R &gt;R R&gt; &gt;R R&gt; <span class="m">4 </span>slot R&gt; &gt;R R&gt; &gt;R R&gt; <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>&quot;special&quot;</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 -0700 https://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&rsquo;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>(&#34;&gt;&#34;)+ =&gt; [[ <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>(&#34;&lt;&#34;)+ =&gt; [[ <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>(&#34;+&#34;)+ =&gt; [[ <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>(&#34;-&#34;)+ =&gt; [[ <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">&#34;.&#34;</span> =&gt; [[ output ]] </span></span><span class="line"><span class="cl">input <span class="nb">= </span><span class="s">&#34;,&#34;</span> =&gt; [[ input ]] </span></span><span class="line"><span class="cl">debug <span class="nb">= </span><span class="s">&#34;#&#34;</span> =&gt; [[ debug ]] </span></span><span class="line"><span class="cl">space <span class="nb">= </span>[ \t\n\r]+ =&gt; [[ <span class="no">f </span>]] </span></span><span class="line"><span class="cl">unknown <span class="nb">= </span>(.) =&gt; [[ <span class="s">&#34;Invalid input&#34;</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">&#34;[&#34;</span> {loop|ops}+ <span class="s">&#34;]&#34;</span> =&gt; [[ <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)* =&gt; [[ <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">&#34; </span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-] </span></span></span><span class="line"><span class="cl"><span class="s"> &gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++ </span></span></span><span class="line"><span class="cl"><span class="s"> .&gt;.+++.------.--------.&gt;+.&gt;. </span></span></span><span class="line"><span class="cl"><span class="s"> &#34;</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&gt;&gt; (&gt;) <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&gt;&gt; (+) <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&gt;&gt; &#39;[ _ [ 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"> [ &lt;brainfuck&gt; ] <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">&#34; </span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-] </span></span></span><span class="line"><span class="cl"><span class="s"> &gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++ </span></span></span><span class="line"><span class="cl"><span class="s"> .&gt;.+++.------.--------.&gt;+.&gt;. </span></span></span><span class="line"><span class="cl"><span class="s"> &#34;</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 &ndash; 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 -0700 https://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 &ndash; and encouraged by a question I was asked recently &ndash; I spent some time thinking about the current process of &ldquo;compiling&rdquo; 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">&#34; </span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-] </span></span></span><span class="line"><span class="cl"><span class="s"> &gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++ </span></span></span><span class="line"><span class="cl"><span class="s"> .&gt;.+++.------.--------.&gt;+.&gt;. </span></span></span><span class="line"><span class="cl"><span class="s"> &#34;</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">&#34; </span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-] </span></span></span><span class="line"><span class="cl"><span class="s"> &gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++ </span></span></span><span class="line"><span class="cl"><span class="s"> .&gt;.+++.------.--------.&gt;+.&gt;. </span></span></span><span class="line"><span class="cl"><span class="s"> &#34;</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"> &lt;brainfuck&gt; <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>(&gt;) <span class="m">7 </span>(+) <span class="m">1 </span>(&gt;) <span class="m">10 </span>(+) <span class="m">1 </span>(&gt;) <span class="m">3 </span>(+) <span class="m">1 </span>(&gt;) <span class="m">1 </span>(+) <span class="m">4 </span>(&lt;) </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>(&gt;) <span class="m">2 </span>(+) (.) <span class="m">1 </span>(&gt;) <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>(&gt;) <span class="m">2 </span>(+) (.) <span class="m">2 </span>(&lt;) <span class="m">15 </span>(+) (.) <span class="m">1 </span>(&gt;) (.) <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>(&gt;) <span class="m">1 </span>(+) (.) <span class="m">1 </span>(&gt;) (.) <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">&#34;;&#34;</span> parse-tokens <span class="nb">concat </span></span></span><span class="line"><span class="cl"> &#39;[ _ 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"> &gt;++++ <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"> &gt;++ <span class="c">! Add 4*2 to Cell #2</span> </span></span><span class="line"><span class="cl"> &gt;+++ <span class="c">! Add 4*3 to Cell #3</span> </span></span><span class="line"><span class="cl"> &gt;+++ <span class="c">! Add 4*3 to Cell #4</span> </span></span><span class="line"><span class="cl"> &gt;+ <span class="c">! Add 4 to Cell #5</span> </span></span><span class="line"><span class="cl"> &lt;&lt;&lt;&lt;- <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"> &gt;+ <span class="c">! Add 1 to Cell #2</span> </span></span><span class="line"><span class="cl"> &gt;+ <span class="c">! Add 1 to Cell #3</span> </span></span><span class="line"><span class="cl"> &gt;- <span class="c">! Subtract 1 from Cell #4</span> </span></span><span class="line"><span class="cl"> &gt;&gt;+ <span class="c">! Add 1 to Cell #6</span> </span></span><span class="line"><span class="cl"> [&lt;] <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"> &lt;- <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"> &gt;&gt;. <span class="c">! Cell #2 has value 72 which is &#39;H&#39;</span> </span></span><span class="line"><span class="cl"> &gt;---. <span class="c">! Subtract 3 from Cell #3 to get 101 which is &#39;e&#39;</span> </span></span><span class="line"><span class="cl"> +++++ ++..+++. <span class="c">! Likewise for &#39;llo&#39; from Cell #3</span> </span></span><span class="line"><span class="cl"> &gt;&gt;. <span class="c">! Cell #5 is 32 for the space</span> </span></span><span class="line"><span class="cl"> &lt;-. <span class="c">! Subtract 1 from Cell #4 for 87 to give a &#39;W&#39;</span> </span></span><span class="line"><span class="cl"> &lt;. <span class="c">! Cell #3 was set to &#39;o&#39; from the end of &#39;Hello&#39;</span> </span></span><span class="line"><span class="cl"> +++.----- -.----- ---. <span class="c">! Cell #3 for &#39;rl&#39; and &#39;d&#39;</span> </span></span><span class="line"><span class="cl"> &gt;&gt;+. <span class="c">! Add 1 to Cell #5 gives us an exclamation point</span> </span></span><span class="line"><span class="cl"> &gt;++. <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&rsquo;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: &gt; </span>[ <span class="m">1 </span>(&gt;) ] } </span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: &lt; </span>[ <span class="m">1 </span>(&lt;) ] } </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">&#34;Invalid input&#34;</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>&lt;brainfuck&gt; [ 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">&#34; </span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-] </span></span></span><span class="line"><span class="cl"><span class="s"> &gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++ </span></span></span><span class="line"><span class="cl"><span class="s"> .&gt;.+++.------.--------.&gt;+.&gt;. </span></span></span><span class="line"><span class="cl"><span class="s"> &#34;</span> interpret-brainfuck </span></span><span class="line"><span class="cl">Hello, World! </span></span></code></pre></div><p>It works! But now I&rsquo;m curious about relative performance. Let&rsquo;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">&#34;,[.,]&#34;</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">&#34;,[.,]&#34;</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">&#34;Compiled&#34;</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">&#34;Interpreted&#34;</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">&#34;Factor&#34;</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">&lt;string&gt; </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">&lt;string&gt; </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">&lt;string&gt; </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&rsquo;s try comparing our &ldquo;Hello, World&rdquo; example &ndash; 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">&#34; </span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-] </span></span></span><span class="line"><span class="cl"><span class="s"> &gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++ </span></span></span><span class="line"><span class="cl"><span class="s"> .&gt;.+++.------.--------.&gt;+.&gt;. </span></span></span><span class="line"><span class="cl"><span class="s"> &#34;</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">&#34; </span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-] </span></span></span><span class="line"><span class="cl"><span class="s"> &gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++ </span></span></span><span class="line"><span class="cl"><span class="s"> .&gt;.+++.------.--------.&gt;+.&gt;. </span></span></span><span class="line"><span class="cl"><span class="s"> &#34;</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&rsquo;s ignoring <em>compilation time</em> in the comparison, and I didn&rsquo;t spend any time on optimizing the interpreted version &ndash; for example, stripping blanks or validating inputs before doing the interpreter loop &ndash; but it&rsquo;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">&#34; </span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-] </span></span></span><span class="line"><span class="cl"><span class="s"> &gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++ </span></span></span><span class="line"><span class="cl"><span class="s"> .&gt;.+++.------.--------.&gt;+.&gt;. </span></span></span><span class="line"><span class="cl"><span class="s"> &#34;</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>&hellip;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 -0700 https://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 &ndash; 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> &ndash; 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&rsquo;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) [ &gt;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">&#34;I&#34;</span> <span class="s">&#34;II&#34;</span> <span class="s">&#34;III&#34;</span> <span class="s">&#34;IV&#34;</span> <span class="s">&#34;IX&#34;</span> <span class="s">&#34;V&#34;</span> <span class="s">&#34;VI&#34;</span> <span class="s">&#34;VII&#34;</span> <span class="s">&#34;VIII&#34;</span> } </span></span></code></pre></div><p>Well, that&rsquo;s <em>almost</em> correct, but the number <code>IX</code> &ndash; the number <code>9</code> &ndash; 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) [ &gt;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&gt; ] sort-by <span class="m">. </span></span></span><span class="line"><span class="cl">{ <span class="s">&#34;I&#34;</span> <span class="s">&#34;II&#34;</span> <span class="s">&#34;III&#34;</span> <span class="s">&#34;IV&#34;</span> <span class="s">&#34;V&#34;</span> <span class="s">&#34;VI&#34;</span> <span class="s">&#34;VII&#34;</span> <span class="s">&#34;VIII&#34;</span> <span class="s">&#34;IX&#34;</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) [ &gt;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&gt; ] sort-by <span class="m">. </span></span></span><span class="line"><span class="cl">{ <span class="s">&#34;I&#34;</span> <span class="s">&#34;II&#34;</span> <span class="s">&#34;III&#34;</span> <span class="s">&#34;IV&#34;</span> <span class="s">&#34;V&#34;</span> <span class="s">&#34;VI&#34;</span> <span class="s">&#34;VII&#34;</span> <span class="s">&#34;VIII&#34;</span> <span class="s">&#34;IX&#34;</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 &ndash; that seems like a lot!</p> <p>Perhaps we could try the <a href="https://en.wikipedia.org/wiki/Schwartzian_transform">Schwartzian transform</a> &ndash; also known as <a href="https://docs.python.org/3/howto/sorting.html#decorate-sort-undecorate">decorate-sort-undecorate</a> &ndash; 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) [ &gt;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&gt; ] <span class="nb">keep </span>] map&gt;alist sort-keys <span class="nb">values </span><span class="m">. </span></span></span><span class="line"><span class="cl">{ <span class="s">&#34;I&#34;</span> <span class="s">&#34;II&#34;</span> <span class="s">&#34;III&#34;</span> <span class="s">&#34;IV&#34;</span> <span class="s">&#34;V&#34;</span> <span class="s">&#34;VI&#34;</span> <span class="s">&#34;VII&#34;</span> <span class="s">&#34;VIII&#34;</span> <span class="s">&#34;IX&#34;</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) [ &gt;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&gt; ] map-sort <span class="m">. </span></span></span><span class="line"><span class="cl">{ <span class="s">&#34;I&#34;</span> <span class="s">&#34;II&#34;</span> <span class="s">&#34;III&#34;</span> <span class="s">&#34;IV&#34;</span> <span class="s">&#34;V&#34;</span> <span class="s">&#34;VI&#34;</span> <span class="s">&#34;VII&#34;</span> <span class="s">&#34;VIII&#34;</span> <span class="s">&#34;IX&#34;</span> } </span></span></code></pre></div><p>Does this make much of a difference? Let&rsquo;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&gt; ] 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&gt; ] 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 [ &gt;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 -0700 https://re.factorcode.org/2025/05/recamans-sequence.html <p>I love reading <a href="https://www.johndcook.com/blog/">John D. Cook&rsquo;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&rsquo;s implement the <a href="https://www.johndcook.com/blog/2025/05/05/recamans-sequence/">Recamán&rsquo;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">&gt;</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&rsquo;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&rsquo;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">&lt;array&gt; </span>:&gt; 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>:&gt; proposal </span></span><span class="line"><span class="cl"> proposal <span class="m">0 </span><span class="nb">&gt; </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&rsquo;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 &gt; proposal a member? not and [ </span></span></span><span class="line"><span class="cl"><span class="gi">+ proposal 0 &gt; [ 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 &gt; proposal a member? not and [ </span></span></span><span class="line"><span class="cl"><span class="gi">+ proposal { [ 0 &gt; ] [ a member? not ] } 1&amp;&amp; [ </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">&lt;array&gt; </span>:&gt; 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">&gt; </span>] [ a <span class="nb">member? not </span>] } 1&amp;&amp; [ </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">&lt;array&gt; </span>:&gt; 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>:&gt; prev </span></span><span class="line"><span class="cl"> prev n <span class="nb">- dup </span>{ [ <span class="m">0 </span><span class="nb">&gt; </span>] [ a <span class="nb">member? not </span>] } 1&amp;&amp; </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">&gt; </span>] [ a <span class="nb">member? not </span>] } 1&amp;&amp; </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">&lt;array&gt; </span>:&gt; 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">&gt; </span>] [ building <span class="nb">get member? not </span>] } 1&amp;&amp; </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 &lt;iota&gt; [ 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">&gt; </span>] [ seen <span class="nb">nth not </span>] } 1&amp;&amp; </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>&lt;bit-vector&gt; &#39;[ _ 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>&lt;bit-vector&gt; &#39;[ _ 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">&gt; </span>] [ seen <span class="nb">nth not </span>] } 1&amp;&amp; </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>&#39;[ </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 &ndash; 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 -0700 https://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&rsquo;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"> &#39;[ @ <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">&#34;20&#34;</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">&#34;20&#34;</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"> [ &#39;[ _ drop-outputs <span class="no">f </span>] ] </span></span><span class="line"><span class="cl"> [ &#39;[ <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">&#34;20&#34;</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"> &#39;[ _ 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"> &#39;[ _ 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">&#34;5&#34;</span> <span class="m">123 </span>} [ string&gt;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">&#34;5&#34;</span> <span class="m">123 </span>} [ string&gt;number ] reject-errors <span class="m">. </span></span></span><span class="line"><span class="cl">{ <span class="s">&#34;5&#34;</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 -0700 https://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">&ldquo;human sorting&rdquo;</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">&ldquo;human sorting improved&rdquo;</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>&quot;x1y&quot;</code> and <code>&quot;x001y&quot;</code> using the original algorithm would consider these to be equal given the same <em>human</em> keys: <code>{ &quot;x&quot; 1 &quot;y&quot; }</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">&#34;x1y&#34;</span> <span class="s">&#34;x001y&#34;</span> human&lt;=&gt; <span class="m">. </span></span></span><span class="line"><span class="cl">+eq+ </span></span></code></pre></div><p>Ned suggests that instead &ndash; if two strings are equal using the human sorting method &ndash; 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">&#34;x1y&#34;</span> <span class="s">&#34;x001y&#34;</span> human&lt;=&gt; <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 -0700 https://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&rsquo;t understand tha math behind it. So, the purpose of this article it&rsquo;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&rsquo;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&rsquo;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 &ldquo;decimal odds&rdquo; are popular in Europe and are the total returns including each original $1 bet if you win. These &ldquo;decimal odds&rdquo; 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 &ldquo;fractional odds&rdquo; 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&gt;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&gt;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 &ndash; 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 &#39;[ odds&gt;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">&#34;%[%.2f, %]&#34;</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 &ldquo;moneyline odds&rdquo; 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&rsquo;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&gt;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">&lt; </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&gt;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">&lt; </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&rsquo;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&gt;odds ] <span class="nb">map </span></span></span><span class="line"><span class="cl"> compute-probs <span class="s">&#34;%[%.2f, %]&#34;</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 &ldquo;parlay odds&rdquo; 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&gt;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&gt;odds ] <span class="nb">map product </span>odds&gt;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&gt;odds odds&gt;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&rsquo;s kinda fun to explore new topics that you don&rsquo;t know much about and to learn!</p> Base256Emoji https://re.factorcode.org/2025/03/base256emoji.html Sat, 22 Mar 2025 08:00:00 -0700 https://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 &ndash; which is unusual for byte encodings (they often seek to reduce the length of an input sequence) &ndash; there are some nice use cases.</p> <p>We&rsquo;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&gt;emoji</span> <span class="s">&#34;🚀🪐☄🛰🌌🌑🌒🌓🌔🌕🌖🌗🌘🌍🌏🌎🐉☀💻🖥\ </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">🚩🍎🍊👼💍📣🥂&#34;</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&gt;base256</span> $[ base256&gt;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">&gt;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&gt;emoji <span class="nb">nth </span>] <span class="s">&#34;&#34;</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&gt;</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&gt;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">&#34;Hello, Factor!&#34;</span> &gt;base256emoji <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;😄✋🍀🍀😓💪😅💓🤤💃😈😓🥺👏&#34;</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">&#34;😄✋🍀🍀😓💪😅💓🤤💃😈😓🥺👏&#34;</span> base256emoji&gt; <span class="s">&#34;&#34;</span> <span class="nb">like </span><span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;Hello, Factor!&#34;</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 &ldquo;<em>address spoofing attacks [that] have mislead tens of thousands of ether, and countless other tokens</em>&rdquo; by using visual emoji-based strings &ndash; 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">&#34;resource:license.txt&#34;</span> md5 checksum-bytes </span></span><span class="line"><span class="cl"> bytes&gt;hex-string <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;ebb5ab617e3a88ed43f7d247c6466d95&#34;</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">&#34;resource:license.txt&#34;</span> md5 checksum-bytes </span></span><span class="line"><span class="cl"> &gt;base256emoji <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;💌💨🤨🤤😠✨😹🥀😏🎼🤠🤩⬇💓🌷🤙&#34;</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 &ldquo;globe&rdquo; emojis (🌍 🌏 🌎)</li> <li>Similar &ldquo;face&rdquo; emojis (🙂 😐 😑 🙁)</li> <li>Similar &ldquo;kiss&rdquo; emojis (😙 😚 😗 😘)</li> <li>Similar &ldquo;star&rdquo; emojis (⭐ 🌟)</li> <li>Similar &ldquo;grin&rdquo; emojis (😀 😃 😄 😁 😆 😅)</li> <li>Similar &ldquo;heart&rdquo; emojis (💔 💗 💕 💖 💘 💙 💜 💚 💛 🖤)</li> <li>Similar &ldquo;hand&rdquo; 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 -0700 https://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">&#34;factorcode.org\r\n&#34;</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">&#34;https://data.iana.org/rdap/&#34;</span> <span class="s">&#34;.json&#34;</span> <span class="nb">surround </span><span class="k">; </span></span></span></code></pre></div><p>We don&rsquo;t want to retrieve these files all the time, so let&rsquo;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&gt;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">&#34;asn&#34;</span> <span class="s">&#34;dns&#34;</span> <span class="s">&#34;ipv4&#34;</span> <span class="s">&#34;ipv6&#34;</span> <span class="s">&#34;object-tags&#34;</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">&#34;.json&#34;</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>&quot;services&quot;</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&#39;</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">&#34;services&#34;</span> <span class="nb">of </span>] <span class="nb">dip </span>&#39;[ [ _ <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"> &#39;[ <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">&#34;dns&#34;</span> bootstrap-get <span class="s">&#34;services&#34;</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">&#34;.&#34;</span> split <span class="nb">dup length </span>&lt;iota&gt; [ <span class="nb">tail </span><span class="s">&#34;.&#34;</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">&#34;factorcode.org&#34;</span> domain-endpoints <span class="m">. </span></span></span><span class="line"><span class="cl">{ <span class="s">&#34;https://rdap.publicinterestregistry.org/rdap/&#34;</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">&#34;google.com&#34;</span> domain-endpoints <span class="m">. </span></span></span><span class="line"><span class="cl">{ <span class="s">&#34;https://rdap.verisign.com/com/v1/&#34;</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">&#34;ipv4&#34;</span> bootstrap-get [ &gt;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">&#34;application/rdap+json&#34;</span> <span class="s">&#34;Accept&#34;</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"> &lt;get-request&gt; 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&gt; <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">&#34;domain/%s&#34;</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">&#34;ip/%s&#34;</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">&#34;factorcode.org&#34;</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">&#34;rdapConformance&#34;</span> ~array~ } { <span class="s">&#34;notices&#34;</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">&#34;:&#34;</span> <span class="nb">print </span><span class="s">&#34; &#34;</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">&#34;: &#34;</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">&#34;&#34;</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 &ndash; for example, the keys could be made more readable, and it doesn&rsquo;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">&#34;factorcode.org&#34;</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">&#34;1.1.1.1&#34;</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 -0700 https://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">&lt;</span><span class="kt">int</span><span class="o">&gt;</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">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</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">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="o">&gt;</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">&lt;</span><span class="kt">int</span><span class="o">&gt;</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">&lt;</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>:&gt; hashmap </span></span><span class="line"><span class="cl"> nums &lt;enumerated&gt; [ </span></span><span class="line"><span class="cl"> <span class="nb">first2 </span>:&gt; <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>:&gt; 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>:&gt; 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>:&gt; 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>:&gt; 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>&#39;[ </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 -0700 https://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&rsquo;s a paper called &ldquo;<a href="https://labdisia.disia.unifi.it/merlini/papers/Derangements.pdf">An analysis of a simple algorithm for random derangements</a>&rdquo; 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">&#34;ABCDEF&#34;</span> &lt;permutations&gt; random <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;FCEBDA&#34;</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">&#34;ABCDEF&#34;</span> &lt;permutations&gt; random <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;DFBCEA&#34;</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>&lt;iota&gt; &lt;permutations&gt; </span></span><span class="line"><span class="cl"> &#39;[ <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 &ndash; 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 &lt;iota&gt; <span class="nb">&gt;array </span>:&gt; 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>:&gt; 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 :&gt; 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&#39;</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 &ndash; of which there are one hundred and forty-eight septillion derangements, give or take &ndash; 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">&#34;ABCDEFGHIJKLMNOPQRSTUVWXYZ&#34;</span> random-derangement <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;CZFABMSUXRQDEHGYJLTPVOIKWN&#34;</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">&#34;ABCD&#34;</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">&#34;BADC&#34;</span> <span class="m">111639 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;BCDA&#34;</span> <span class="m">110734 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;BDAC&#34;</span> <span class="m">110682 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;CADB&#34;</span> <span class="m">111123 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;CDAB&#34;</span> <span class="m">111447 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;CDBA&#34;</span> <span class="m">111147 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;DABC&#34;</span> <span class="m">111215 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;DCAB&#34;</span> <span class="m">111114 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;DCBA&#34;</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 -0700 https://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>&lt;iota&gt; [ <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>&lt;derangement-iota&gt;</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">&lt;derangement-iota&gt;</span> <span class="nf">( </span><span class="nv">seq</span> <span class="nf">-- </span><span class="nv">&lt;iota&gt;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="nb">length </span>subfactorial &lt;iota&gt; <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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ [ &lt;derangement-iota&gt; ] [ <span class="nb">length </span>&lt;iota&gt; <span class="nb">&gt;array </span>] [ ] <span class="nb">tri </span>] <span class="nb">dip </span></span></span><span class="line"><span class="cl"> &#39;[ <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&#39;</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"> &#39;[ _ <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>&quot;ABCD&quot;</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">&#34;ABCD&#34;</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">&#34;BADC&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;BCDA&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;BDAC&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;CADB&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;CDAB&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;CDBA&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;DABC&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;DCAB&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;DCBA&#34;</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 -0700 https://re.factorcode.org/2024/12/zen-of-factor.html <p>Years ago, I remember reading a blog post called &ldquo;<a href="http://factor-language.blogspot.com/2006/06/why-is-groovy-is-big.html">Why is Groovy is big?</a>&rdquo; 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&rsquo;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 &#34;import this&#34; 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&#39;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&#39;re Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it&#39;s a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let&#39;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&#39;t work, fix it. If you don&#39;t like it, change it. Today&#39;s beginner is tomorrow&#39;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 -0700 https://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&rsquo;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&gt;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">SYNTAX: </span>&lt;WATCH </span></span><span class="line"><span class="cl"> <span class="no">\ WATCH&gt;</span> parse-until &gt;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>&lt;WATCH <span class="nb">+ </span>WATCH&gt; <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&rsquo;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&rsquo;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 -0700 https://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> &ndash; called the <em>Listener</em> &ndash; 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> &ndash; or <code>Cmd</code> on <a href="https://www.apple.com/macos">macOS</a> &ndash; 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 -0700 https://re.factorcode.org/2024/11/finding-subsequences.html <p>Recently, I&rsquo;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 &ldquo;<em>accumulation of values in an array&hellip; to find all occurences (their position) of a subseq in a seq</em>&rdquo;. 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 &ndash; 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">&#34;abcabcabc&#34;</span> <span class="s">&#34;abcabc&#34;</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&rsquo;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&rsquo;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&rsquo;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 &ldquo;<em>take an index to start from and find the next matching subseq index</em>&rdquo;, which looks an awful lot like <a href="https://docs.factorcode.org/content/word-subseq-index-from%2Csequences.html">subseq-index-from</a> &ndash; 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>&#39;[ </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>&#39;[ </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>&#39;[ </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>&#39;[ _ _ 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>&#39;[ <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 &ldquo;<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>&rdquo; 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"> &#39;[ _ _ 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>:&gt; accum </span></span><span class="line"><span class="cl"> <span class="m">0 </span>:&gt; 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&rsquo;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&rsquo;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 -0700 https://re.factorcode.org/2024/11/removing-subdomains.html <p>There was an interesting question on the <a href="https://unix.stackexchange.com">Unix &amp; 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&rsquo;re curious about what common subdomains are, we can turn to the <a href="https://github.com/danielmiessler/SecLists">SecLists</a> project &ndash; described as a &ldquo;<em>security tester&rsquo;s companion</em>&rdquo; &ndash; 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">&#34;https://raw.githubusercontent.com/danielmiessler/SecLists/refs/heads/master/Discovery/DNS/subdomains-top1million-5000.txt&#34;</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 &ldquo;top 10&rdquo; 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">&#34;www&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;mail&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;ftp&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;localhost&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;webmail&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;smtp&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;webdisk&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;pop&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;cpanel&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;whm&#34;</span> </span></span><span class="line"><span class="cl">} </span></span></code></pre></div><p>You could remove &ldquo;common subdomains&rdquo; &ndash; adding a dot to make sure we only strip a full subdomain &ndash; 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> top-5000-subdomains [ <span class="s">&#34;.&#34;</span> <span class="nb">append </span>] <span class="nb">map </span>&#39;[ _ [ ?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">&#34;www.mail.ftp.localhost.factorcode.org&#34;</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">&#34;factorcode.org&#34;</span> </span></span></code></pre></div><p>That works pretty well, but it&rsquo;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&#39;</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>&#39;[ </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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ <span class="s">&#34;.&#34;</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">&#34;a.b.c&#34;</span> <span class="s">&#34;b.c&#34;</span> <span class="s">&#34;c.d.e&#34;</span> <span class="s">&#34;e.f&#34;</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">&#34;b.c&#34;</span> <span class="s">&#34;c.d.e&#34;</span> <span class="s">&#34;e.f&#34;</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&gt;a-names <span class="nb">empty? not </span>] </span></span><span class="line"><span class="cl"> [ dns-AAAA-query message&gt;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">&#34;re.factorcode.org&#34;</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">&#34;not-valid.factorcode.org&#34;</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">&#34;.&#34;</span> split <span class="nb">dup length </span><span class="m">1 </span>[-] &lt;iota&gt; [ <span class="nb">tail </span><span class="s">&#34;.&#34;</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">&#34;a.b.c.com&#34;</span> split-domain <span class="m">. </span></span></span><span class="line"><span class="cl">{ <span class="s">&#34;a.b.c.com&#34;</span> <span class="s">&#34;b.c.com&#34;</span> <span class="s">&#34;c.com&#34;</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&#39;</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">&#34;a.b.c.d.factorcode.org&#34;</span> remove-subdomains <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;factorcode.org&#34;</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">&#34;sorting.cr.yp.to&#34;</span> remove-subdomains <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;cr.yp.to&#34;</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&rsquo;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 -0700 https://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 &ldquo;<em>A Language A Day</em>&rdquo;, which is a collection of brief overviews to 21 programming languages &ndash; 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 -0700 https://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">&#34;;&#34;</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&rsquo;re defining new syntax</li> <li><code>CONSTANTS:</code> is the name of our new syntax word</li> <li><code>&quot;;&quot;</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&rsquo;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&rsquo;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&rsquo;s neat!</p> Base16 Themes https://re.factorcode.org/2024/10/base16-themes.html Thu, 17 Oct 2024 08:00:00 -0700 https://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&rsquo;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">&#34;solarized-dark&#34;</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">&#34;greenscreen&#34;</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 -0700 https://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&rsquo;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 &ndash; 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&rsquo;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">&lt;lazy&gt;</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>&#39;[ _ &lt;lazy&gt; <span class="nb">suffix! </span>] define-temp-syntax ] H{ } <span class="nb">map&gt;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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>lazy? [ token&gt;&gt; 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&gt;&gt; </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"> &#39;[ _ 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&rsquo;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&rsquo;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&rsquo;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 -0700 https://re.factorcode.org/2024/09/battlesnake.html <p><a href="https://play.battlesnake.com">Battlesnake</a> is <em>&ldquo;a competitive game where your code is the controller&rdquo;</em>. In particular, in answering the question &ldquo;<a href="https://docs.battlesnake.com">What is Battlesnake?</a>&rdquo;, 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&rsquo;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 &ldquo;Coding Badly&rdquo; 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&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;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&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;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&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;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 -0700 https://re.factorcode.org/2024/09/factor-0-100-now-available.html <p><em>&ldquo;Life can only be understood backwards; but it must be lived forwards.&rdquo; — Kierkegaard</em></p> <p>I&rsquo;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&gt;rad</code> and <code>rad&gt;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 &ldquo;tee&rsquo;ing&rdquo; stream utility</li> <li><a href="https://docs.factorcode.org/content/vocab-images.viewer.scaling.html">images.viewer.scaling</a>: adding a &ldquo;scaling image&rdquo; 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 &ldquo;running statistics&rdquo; and &ldquo;running regression&rdquo; 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 &ldquo;prefixed&rdquo; sequence</li> <li><a href="https://docs.factorcode.org/content/vocab-sequences.suffixed.html">sequences.suffixed</a>: virtual &ldquo;suffixed&rdquo; 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&lt;=</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 &ldquo;~&rdquo; 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&quot; column&quot;</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>&lt;t:meta&gt;</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&quot;</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>&lt;connected-pair&gt;</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&rsquo;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>&lt;k-permutations&gt;</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&gt;rad</code> and <code>rad&gt;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>&lt;matrix-by&gt;</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>&gt;digits</code> and <code>digits&gt;</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>&lt;cheapest-chat-completion&gt;</code> for ease-of-use with &ldquo;gpt-4o-mini&rdquo;, 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 &ldquo;fingerbreadth&rdquo; 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>&lt;t:meta&gt;</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 -0700 https://re.factorcode.org/2024/08/cash-register.html <p>Building a &ldquo;cash register&rdquo; 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&rsquo;s Javascript Algorithms and Data Structures Project &ldquo;Cash Register&rdquo;</a> or <a href="https://www.codecademy.com/forum_questions/5407f855282ae35fc700033e">codecademy&rsquo;s &ldquo;Building a Cash Register&rdquo;</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 &ndash; the <a href="https://en.wikipedia.org/wiki/United_States_dollar">&ldquo;buck&rdquo;</a>.</li> <li>We can make change in various units &ndash; 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">&#34;$100&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">5000 </span><span class="s">&#34;$50&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">2000 </span><span class="s">&#34;$20&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">1000 </span><span class="s">&#34;$10&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">500 </span><span class="s">&#34;$5&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">200 </span><span class="s">&#34;$2&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">100 </span><span class="s">&#34;$1&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">25 </span><span class="s">&#34;quarters&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">10 </span><span class="s">&#34;dimes&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">5 </span><span class="s">&#34;nickels&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">1 </span><span class="s">&#34;pennies&#34;</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">&#34;$%.2f\n&#34;</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">&#34;CHANGE: &#34;</span> <span class="nb">write dup </span>$. make-change [ </span></span><span class="line"><span class="cl"> &#39;[ _ <span class="s">&#34;%d of %s\n&#34;</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">&#34;OWED: &#34;</span> <span class="nb">write </span>owed <span class="nb">get-global </span>$. </span></span><span class="line"><span class="cl"> <span class="s">&#34;PAID: &#34;</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">&#34;CHARGE: &#34;</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">&#34;PAY: &#34;</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">&gt;= </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">&#34;CANCEL&#34;</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">&#34;$&#34;</span> ?head <span class="nb">drop </span>string&gt;number <span class="m">100 </span><span class="nb">* </span>round <span class="nb">&gt;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">&#34;balance&#34;</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">&#34;Display current balance.&#34;</span> } </span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">&#34;b&#34;</span> } } } </span></span><span class="line"><span class="cl"> T{ command </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;charge&#34;</span> } </span></span><span class="line"><span class="cl"> { quot [ parse-$ charge ] } </span></span><span class="line"><span class="cl"> { help <span class="s">&#34;Charge an item.&#34;</span> } </span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">&#34;c&#34;</span> } } } </span></span><span class="line"><span class="cl"> T{ command </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;pay&#34;</span> } </span></span><span class="line"><span class="cl"> { quot [ parse-$ pay ] } </span></span><span class="line"><span class="cl"> { help <span class="s">&#34;Pay with money.&#34;</span> } </span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">&#34;p&#34;</span> } } } </span></span><span class="line"><span class="cl"> T{ command </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;cancel&#34;</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">&#34;Cancel transaction.&#34;</span> } </span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">&#34;x&#34;</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">&#34;Welcome to the Cash Register!&#34;</span> <span class="s">&#34;$&gt;&#34;</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! $&gt; c 10.23 CHARGE: $10.23 OWED: $10.23 PAID: $0.00 $&gt; c 15.37 CHARGE: $15.37 OWED: $25.60 PAID: $0.00 $&gt; 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 &ldquo;complete&rdquo; or &ldquo;real-world&rdquo; 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 -0700 https://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 -0700 https://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 &ndash; we suggest starting with <code>Full environment</code> and then reducing until the program breaks &ndash; 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">&#34;bitguessr&#34;</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">&#34; libraylib.dylib&#34;</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>: &lt;B5534AF8-58E9-3F59-A5DE-F33164570F6B&gt; ./bitguessr.app/Contents/Frameworks/libraylib.dylib </span></span></code></pre></div><p>Yes, it seems to be.</p> <p>Let&rsquo;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&rsquo;s start with the library &ndash; 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 &ldquo;runtime path&rdquo; 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">&#34;@rpath/libraylib.dylib&#34;</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">&#34; libraylib.dylib&#34;</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&rsquo;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 &ldquo;runtime path&rdquo; 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">&#34;@executable_path/../Frameworks&#34;</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&rsquo;s try again&hellip; 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 -0700 https://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&rsquo;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 &ldquo;<em>about 2 years ago</em>&rdquo;, so perhaps doesn&rsquo;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">&#34;https://rosettacode.org/w/api.php&#34;</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"> &#39;H{ { <span class="s">&#34;list&#34;</span> <span class="s">&#34;categorymembers&#34;</span> } { <span class="s">&#34;cmtitle&#34;</span> _ } } </span></span><span class="line"><span class="cl"> query [ <span class="s">&#34;title&#34;</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">&#34;Category:&#34;</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">&#34;Category:Solutions_by_Programming_Task&#34;</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">&#34;Category:Draft_Programming_Tasks&#34;</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">&#34;Sieve_of_Eratosthenes&#34;</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">&#34;=={{header&#34;</span> <span class="nb">over subseq? </span>[ </span></span><span class="line"><span class="cl"> <span class="s">&#34;&#34;</span> <span class="s">&#34;=={{header&#34;</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>&lt;syntaxhighlight&gt;</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">&#34;&lt;syntaxhighlight lang=\&#34;&#34;</span> <span class="s">&#34;\&#34;&gt;&#34;</span> <span class="nb">surround </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;&lt;/syntaxhighlight&gt;&#34;</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">&#34;! &#34;</span> <span class="nb">prepend </span>] <span class="nb">map </span><span class="s">&#34;\n&#34;</span> <span class="nb">join </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;factor&#34;</span> get-code <span class="s">&#34;\n\n&#34;</span> <span class="nb">glue </span><span class="s">&#34;\n&#34;</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">&#34;10001th_prime&#34;</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&rsquo;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"> &gt;lower R/ --+/ <span class="s">&#34;-&#34;</span> re-replace [ <span class="sc">CHAR: - </span><span class="nb">= </span>] <span class="nb">trim </span><span class="s">&#34;.factor&#34;</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">&#34;vocab:rosetta-code/solutions&#34;</span> [ </span></span><span class="line"><span class="cl"> [ get-solution ] </span></span><span class="line"><span class="cl"> [ task-path &#39;[ _ 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 -0700 https://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&rsquo;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 &ndash; and pretty much immediately afterwards &ndash; 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&rsquo;s not a small amount of code &ndash; it&rsquo;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&rsquo;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 -0700 https://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&rsquo;t able to maintain the &ldquo;authoritative&rdquo; 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&rsquo;t actively maintaining.</p> <p>That move is now complete, but don&rsquo;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 -0700 https://re.factorcode.org/2024/07/bitguessr.html <p>The <a href="https://www.raylib.com">Raylib</a> library is a &ldquo;<em>simple and easy-to-use library to enjoy videogames programming</em>&rdquo; 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> &ndash; 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 &ndash; I love this use of Factor! &ndash; 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 -0700 https://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&rsquo;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&rsquo;s a good workflow where I can write the code iteratively.</p> </blockquote> <p>Let&rsquo;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&rsquo;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 &ndash; 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 &ldquo;get&rdquo; 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 &ldquo;under&rdquo; 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&rsquo;re designed with a specific word order; typically things that are &ldquo;parameters&rdquo; 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&rsquo;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 -0700 https://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 &ndash; 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> &lt; <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&rsquo;s always things to add!</p> Magic Forest https://re.factorcode.org/2024/06/magic-forest.html Sat, 29 Jun 2024 06:00:00 -0700 https://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 &ldquo;Math Kangaroo&rdquo; 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. (&quot;<em>The stronger animal eats the weaker one</em>&quot;.) 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&rsquo;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>&ldquo;fastest&rdquo;</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">&lt;forest&gt;</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">&gt;forest&lt;</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&gt;&gt; ] [ wolves&gt;&gt; ] [ lions&gt;&gt; ] <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"> &gt;forest&lt; { [ <span class="nb">pick </span><span class="m">0 </span><span class="nb">&gt; </span>] [ <span class="nb">over </span><span class="m">0 </span><span class="nb">&gt; </span>] } 0&amp;&amp; </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>&lt;forest&gt; ] [ <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"> &gt;forest&lt; { [ <span class="nb">pick </span><span class="m">0 </span><span class="nb">&gt; </span>] [ <span class="nb">dup </span><span class="m">0 </span><span class="nb">&gt; </span>] } 0&amp;&amp; </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>&lt;forest&gt; ] [ <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"> &gt;forest&lt; { [ <span class="nb">dup </span><span class="m">0 </span><span class="nb">&gt; </span>] [ <span class="nb">over </span><span class="m">0 </span><span class="nb">&gt; </span>] } 0&amp;&amp; </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>&lt;forest&gt; ] [ <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&#39;</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&#39;</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>&lt;hash-set&gt; ] <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"> &gt;forest&lt; <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 &ndash; 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&rsquo;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&hellip;</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>} &gt;forest&lt; 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>} &gt;forest&lt; 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>} &gt;forest&lt; 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&hellip;</p> Man or Boy https://re.factorcode.org/2024/06/man-or-boy.html Wed, 26 Jun 2024 08:00:00 -0700 https://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>&ldquo;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.&rdquo;</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&rsquo;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">&lt;=</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>&ldquo;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.&rdquo;</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">&lt;= </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>:&gt; 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&rsquo;s &ldquo;man or boy&rdquo; 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>&ldquo;retain stack overflow&rdquo;</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&rsquo;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">&lt;= </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&rsquo;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 -0700 https://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> &ndash; <code>quit</code> &ndash; 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">&#34;help&#34;</span><span class="p">,</span> <span class="s2">&#34;copyright&#34;</span><span class="p">,</span> <span class="s2">&#34;credits&#34;</span> <span class="ow">or</span> <span class="s2">&#34;license&#34;</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">&gt;&gt;&gt;</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">&gt;&gt;&gt;</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&rsquo;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 -0700 https://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&rsquo;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">&lt;</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">&lt;</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">&#34;fork&#34;</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">&gt; </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">&gt; </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 :&gt; <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">&lt; </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">&lt; </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&rsquo;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&rsquo;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 -0700 https://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 &ldquo;interpolating variable values into strings&rdquo; &ndash; using words such as <code>interpolate</code> as well as the more recently added <em>interpolate string</em> syntax <code>I&quot; &quot;</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">&#34;${:011.5f}&#34;</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">&#34;${:2d} ${:3d} ${:4d}\n&#34;</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">&#34;n&#34;</span> <span class="m">42 </span>} } [ </span></span><span class="line"><span class="cl"> <span class="s">&#34;Hello, Worker #${n:06x}!&#34;</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 -0700 https://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 -&gt; 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 -&gt; whatever) -&gt; (whatever, input -&gt; 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&rsquo;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> &ndash; 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"> &#39;[ { [ <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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> &#39;[ @ <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&rsquo;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>&#39;[ </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&gt;&gt; ] <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&#39;</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>&#39;[ @ <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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> &#39;[ <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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [let <span class="no">f </span>:&gt; 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&#39;</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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [let <span class="no">f </span>:&gt; 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [let <span class="no">f </span>:&gt; 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [let <span class="no">f </span>:&gt; s! [ HS{ } <span class="nb">clone </span>s! ] % </span></span><span class="line"><span class="cl"> &#39;[ [ 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>&hellip;</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">&gt; </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">&gt; </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&rsquo;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 -0700 https://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&rsquo;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 &ndash; which generates a &ldquo;byte-by-byte copy of the given object&rdquo; &ndash; 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&rsquo;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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> object&gt;bytes bytes&gt;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 -0700 https://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&rsquo;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">&#34;&#34;</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">&#34;&#34;</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">&#34;&#34;</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">&#34;&#34;</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>&quot;hello-world&quot; 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&rsquo;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 -0700 https://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&rsquo;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>&lt;range&gt; </span></span><span class="line"><span class="cl"> <span class="m">1 40 3 </span>&lt;range&gt; 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>&lt;range&gt; </span></span><span class="line"><span class="cl"> <span class="m">1 40 3 </span>&lt;range&gt; 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 &gt;forward-range&lt; :&gt; <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 &gt;forward-range&lt; :&gt; <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 :&gt; <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>:&gt; <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>:&gt; b </span></span><span class="line"><span class="cl"> step1 step2 lcm :&gt; 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 :&gt; 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 :&gt; n </span></span><span class="line"><span class="cl"> m n a &lt;range&gt; </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 &ndash; and is often the case when you are dealing with a large number of items on the stack &ndash; it isn&rsquo;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"> [ &gt;forward-range&lt; ] <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"> [ &#39;[ [ _ <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"> [ &#39;[ <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>&lt;range&gt; </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&rsquo;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"> [ &gt;forward-range&lt; ] <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"> [ &#39;[ [ _ <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"> [ &#39;[ <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>&lt;range&gt; <span class="k">; </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&rsquo;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 -0700 https://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>&gt;lower { </span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>{ <span class="s">&#34;-h&#34;</span> <span class="s">&#34;--host&#34;</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">&#34;-p&#34;</span> <span class="s">&#34;--port&#34;</span> } <span class="nb">member? </span>] [ <span class="nb">unclip </span>string&gt;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">&#34;-u&#34;</span> <span class="s">&#34;--username&#34;</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">&#34;-w&#34;</span> <span class="s">&#34;--password&#34;</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">&#34;host&#34;</span> <span class="nb">get </span><span class="s">&#34;127.0.0.1&#34;</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">&#34;port&#34;</span> <span class="nb">get </span>[ string&gt;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">&#34;username&#34;</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">&#34;password&#34;</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&rsquo;t easily allow for boolean values that default to true, and doesn&rsquo;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">&#34;--host&#34;</span> } </span></span><span class="line"><span class="cl"> { help <span class="s">&#34;set the hostname&#34;</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">&#34;127.0.0.1&#34;</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">&#34;--port&#34;</span> } </span></span><span class="line"><span class="cl"> { help <span class="s">&#34;set the port&#34;</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">&#34;--username&#34;</span> } </span></span><span class="line"><span class="cl"> { help <span class="s">&#34;set the username&#34;</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">&#34;--password&#34;</span> } </span></span><span class="line"><span class="cl"> { help <span class="s">&#34;set the password&#34;</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 &#39;port&#39; $ ./factor -run=stomp.cli --port asdf ERROR: Invalid value &#39;asdf&#39; for option &#39;port&#39; $ ./factor -run=stomp.cli --prot ERROR: Unknown option &#39;prot&#39; $ ./factor -run=stomp.cli a b c d ERROR: Unrecognized arguments: a b c d $ ./factor -run=stomp.cli -h ERROR: The option &#39;h&#39; 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> &ndash; give it a try!</p> STOMP https://re.factorcode.org/2024/05/stomp.html Thu, 09 May 2024 08:00:00 -0700 https://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>&ldquo;Simple Text Oriented Messaging Protocol&rdquo;</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">&lt;frame&gt;</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&gt;&gt; <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">&#34;:&#34;</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">&#34;content-length&#34;</span> <span class="nb">of </span>string&gt;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&gt;&gt; <span class="nb">print </span>] </span></span><span class="line"><span class="cl"> [ headers&gt;&gt; [ <span class="s">&#34;:&#34;</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&gt;&gt; [ <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">&#34;CONNECT&#34;</span> &lt;frame&gt; </span></span><span class="line"><span class="cl"> stomp-username <span class="nb">get </span>[ <span class="s">&#34;login&#34;</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">&#34;passcode&#34;</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&gt;&gt; <span class="s">&#34;CONNECTED&#34;</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">&#34;SEND&#34;</span> &lt;frame&gt; </span></span><span class="line"><span class="cl"> body &gt;&gt;body </span></span><span class="line"><span class="cl"> destination <span class="s">&#34;destination&#34;</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">&#34;SEND&#34;</span> &lt;frame&gt; </span></span><span class="line"><span class="cl"> destination <span class="s">&#34;destination&#34;</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 &gt;&gt;body ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;content-type&#34;</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">&#34;SUBSCRIBE&#34;</span> &lt;frame&gt; </span></span><span class="line"><span class="cl"> destination <span class="s">&#34;destination&#34;</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">&#34;UNSUBSCRIBE&#34;</span> &lt;frame&gt; </span></span><span class="line"><span class="cl"> destination <span class="s">&#34;destination&#34;</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">&#34;stomp writer&#34;</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">&#34;127.0.0.1&#34;</span> <span class="m">61613 </span>utf8 [ </span></span><span class="line"><span class="cl"> &lt;mailbox&gt; [ [ <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> $[ &lt;mailbox&gt; ] </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">&#34;send&#34;</span> } </span></span><span class="line"><span class="cl"> { quot [ <span class="s">&#34; &#34;</span> split1 stomp-send put-frame ] } </span></span><span class="line"><span class="cl"> { help <span class="s">&#34;Send a message to a destination in the messaging system.&#34;</span> } } </span></span><span class="line"><span class="cl"> T{ command </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;sendfile&#34;</span> } </span></span><span class="line"><span class="cl"> { quot [ <span class="s">&#34; &#34;</span> split1 stomp-sendfile put-frame ] } </span></span><span class="line"><span class="cl"> { help <span class="s">&#34;Send a file to a destination in the messaging system.&#34;</span> } } </span></span><span class="line"><span class="cl"> T{ command </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;subscribe&#34;</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">&#34;Subscribe to a destination.&#34;</span> } } </span></span><span class="line"><span class="cl"> T{ command </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;unsubscribe&#34;</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">&#34;Unsubscribe from a destination.&#34;</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">&#34;127.0.0.1&#34;</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>&lt;inet4&gt; 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">&#34;Welcome to STOMP!&#34;</span> <span class="s">&#34;STOMP&gt;&#34;</span> &lt;command-loop&gt; </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&gt; subscribe /queue/test STOMP&gt; send /queue/test hello world T{ frame { command &#34;MESSAGE&#34; } { headers { { &#34;expires&#34; &#34;0&#34; } { &#34;destination&#34; &#34;/queue/test&#34; } { &#34;subscription&#34; &#34;1&#34; } { &#34;priority&#34; &#34;4&#34; } { &#34;message-id&#34; &#34;ID\\chostname-59660-1715273573218-3\\c3\\c-1\\c1\\c1&#34; } { &#34;timestamp&#34; &#34;1715276926454&#34; } } } { body &#34;hello world&#34; } } </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 &ndash; 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 -0700 https://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">! &lt;10 seconds</span> </span></span><span class="line"><span class="cl"> { <span class="s">&#34;A single frame of a film&#34;</span> <span class="m">100 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;It would take light to go around the Earth&#34;</span> <span class="m">133 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;A blink of an eye&#34;</span> <span class="m">400 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;The time it takes light to reach Earth from the moon&#34;</span> <span class="m">1255 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;The fastest Formula 1 pit stop&#34;</span> <span class="m">1820 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;The fastest 1/4 mile drag race time&#34;</span> <span class="m">3580 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;The fastest Rubik&#39;s cube solve&#34;</span> <span class="m">4221 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;The fastest 40-yard time at the NFL Combine&#34;</span> <span class="m">4240 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;The fastest 1 liter beer chug&#34;</span> <span class="m">4370 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;A skippable Youtube ad&#34;</span> <span class="m">5000 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;A full bull ride&#34;</span> <span class="m">8000 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;The fastest 100m sprint&#34;</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">&#34;The Wright Brothers first flight&#34;</span> <span class="m">12000 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;The fastest 200m sprint&#34;</span> <span class="m">19190 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;The fastest 50m freestyle swim lap&#34;</span> <span class="m">21300 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;The Westminster Kennel Club dog agility record&#34;</span> <span class="m">28440 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;A typical television ad&#34;</span> <span class="m">30000 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;The fastest NASCAR lap at Daytona&#34;</span> <span class="m">40364 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;The fastest 400m sprint&#34;</span> <span class="m">43030 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;The fastest NASCAR lap at Talladega&#34;</span> <span class="m">44270 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;The fastest 100m freestyle swim lap&#34;</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">&lt; </span></span></span><span class="line"><span class="cl"> [ <span class="s">&#34;%.1f seconds&#34;</span> sprintf ] </span></span><span class="line"><span class="cl"> [ seconds duration&gt;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 &lt; </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">&#34;%s (%s)\n&#34;</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&#39;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> &lt; <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">&lt;meeting-gadget&gt;</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>:&gt; meeting </span></span><span class="line"><span class="cl"> COLOR: #f7f08b &lt;solid&gt; &gt;&gt;interior </span></span><span class="line"><span class="cl"> <span class="m">0 </span>&gt;&gt;total </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="s">&#34;&#34;</span> &lt;label&gt; :&gt; current-text </span></span><span class="line"><span class="cl"> <span class="s">&#34;&#34;</span> &lt;label&gt; :&gt; current-time </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="s">&#34;&#34;</span> &lt;label&gt; :&gt; total-time </span></span><span class="line"><span class="cl"> <span class="s">&#34;&#34;</span> &lt;label&gt; :&gt; 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"> [ &lt;label&gt; ] <span class="nb">bi@ </span>:&gt; <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&gt;&gt; </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&gt;milliseconds <span class="nb">+ </span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>meeting total&lt;&lt; </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&gt;&gt; <span class="nb">= </span>[ <span class="nb">2drop </span>] [ </span></span><span class="line"><span class="cl"> next-text string&gt;&gt; current-text string&lt;&lt; </span></span><span class="line"><span class="cl"> next-time string&gt;&gt; current-time string&lt;&lt; </span></span><span class="line"><span class="cl"> human-time </span></span><span class="line"><span class="cl"> next-time string&lt;&lt; </span></span><span class="line"><span class="cl"> next-text string&lt;&lt; </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&lt;&lt; </span></span><span class="line"><span class="cl"> ] <span class="no">f </span><span class="m">100 </span>milliseconds &lt;timer&gt; &gt;&gt;timer </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> vertical &lt;track&gt; </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">&#34;This meeting is longer than...&#34;</span> &lt;labeled-gadget&gt; <span class="no">f </span>track-add </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> vertical &lt;track&gt; </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">&#34;It has been going on for...&#34;</span> &lt;labeled-gadget&gt; <span class="no">f </span>track-add </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> vertical &lt;track&gt; </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">&#34;The next milestone is...&#34;</span> &lt;labeled-gadget&gt; <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">&#34;Start&#34;</span> &lt;label&gt; :&gt; start-label </span></span><span class="line"><span class="cl"> <span class="s">&#34;Reset&#34;</span> &lt;label&gt; :&gt; reset-label </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> horizontal &lt;track&gt; </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&gt;&gt; [ </span></span><span class="line"><span class="cl"> <span class="m">0 </span>&gt;&gt;total now timestamp&gt;hms </span></span><span class="line"><span class="cl"> <span class="s">&#34;Started at &#34;</span> <span class="nb">prepend </span>start-time string&lt;&lt; </span></span><span class="line"><span class="cl"> ] <span class="nb">unless </span></span></span><span class="line"><span class="cl"> now &gt;&gt;start </span></span><span class="line"><span class="cl"> timer&gt;&gt; <span class="nb">dup </span>thread&gt;&gt; </span></span><span class="line"><span class="cl"> [ stop-timer <span class="s">&#34;Resume&#34;</span> start-label string&lt;&lt; ] </span></span><span class="line"><span class="cl"> [ start-timer <span class="s">&#34;Pause&#34;</span> start-label string&lt;&lt; ] <span class="nb">if </span></span></span><span class="line"><span class="cl"> ] &lt;border-button&gt; <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>&gt;&gt;total <span class="no">f </span>&gt;&gt;start timer&gt;&gt; stop-timer </span></span><span class="line"><span class="cl"> <span class="s">&#34;Start&#34;</span> start-label string&lt;&lt; </span></span><span class="line"><span class="cl"> <span class="s">&#34;&#34;</span> current-text string&lt;&lt; </span></span><span class="line"><span class="cl"> <span class="s">&#34;&#34;</span> current-time string&lt;&lt; </span></span><span class="line"><span class="cl"> <span class="s">&#34;&#34;</span> total-time string&lt;&lt; </span></span><span class="line"><span class="cl"> <span class="s">&#34;&#34;</span> start-time string&lt;&lt; </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&lt;&lt; </span></span><span class="line"><span class="cl"> next-text string&lt;&lt; </span></span><span class="line"><span class="cl"> ] &lt;border-button&gt; <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">&#34;Time My Meeting&#34;</span> } } </span></span><span class="line"><span class="cl"> &lt;meeting-gadget&gt; &gt;&gt;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 -0700 https://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 -0700 https://re.factorcode.org/2024/02/reverse-vowels.html <p>Our task today is to &ldquo;reverse vowels of a string&rdquo;. 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&rsquo;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&rsquo;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> &ndash; currently in the miscellaneous <a href="https://docs.factorcode.org/content/vocab-sequences.extras.html">sequences.extras vocabulary</a> &ndash; 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">&#34;hello&#34;</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&rsquo;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">&lt;reversed&gt; </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>&gt;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">&#34;factor&#34;</span> reverse-vowels <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;foctar&#34;</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">&#34;concatenative&#34;</span> reverse-vowels <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;cencitanetavo&#34;</span> </span></span></code></pre></div><p>Pretty cool!</p> <h3 id="lets-reverse-the-vowels-maintain-the-case">Let&rsquo;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&rsquo;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&#39;</span> <span class="nv">b&#39;</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&gt;upper ] [ ch&gt;lower ] <span class="nb">bi* </span>] } </span></span><span class="line"><span class="cl"> { { <span class="no">f t </span>} [ [ ch&gt;lower ] [ ch&gt;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"> [ &#39;[ _ <span class="nb">nth </span>] <span class="nb">bi@ </span>swap-case ] </span></span><span class="line"><span class="cl"> [ &#39;[ _ <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">&lt;reversed&gt; </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>&gt;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">&#34;FActor&#34;</span> reverse-vowels-case <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;FOctar&#34;</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 -0700 https://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 &ldquo;shortest decimal representation&rdquo; 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&rsquo;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">&gt;&gt;&gt;</span> <span class="nb">float</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="s1">&#39;0x1.1ffffffffffffp7&#39;</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">&gt;&gt;&gt;</span> <span class="nb">float</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="s1">&#39;0x1.2p7&#39;</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">&gt;&gt;&gt;</span> <span class="nb">float</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="s1">&#39;0x1.2000000000001p7&#39;</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 &ldquo;Dragonbox&rdquo; 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 &ldquo;modern formatting library&rdquo; 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> &ndash; providing a very readable and understandable and <a href="https://re.factorcode.org/2011/07/concatenative-thinking.html">nicely concatenative</a> version &ndash; 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&rsquo;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 -0700 https://re.factorcode.org/2024/02/divmods.html <p>There&rsquo;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">&#39;required at least one divisor&#39;</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">&gt;&gt;&gt;</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"> [ &#39;[ _ <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 -0700 https://re.factorcode.org/2024/01/crontab.html <p><a href="https://cron.com">Cron</a> might be the latest, greatest, and coolest &ldquo;<em>next-generation calendar</em>&rdquo; as well as now a product called <a href="https://www.notion.so/product/calendar">Notion Calendar</a>. But in the good ol&rsquo; 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> &ndash; the cron daemon &ndash; 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) # │ │ │ │ │ # │ │ │ │ │ # * * * * * &lt;command to execute&gt; </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&rsquo;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">&#34;*&#34;</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">&#34;,&#34;</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">&#34;/&#34;</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&gt;number &lt;range&gt; <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">&#34;-&#34;</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">&#34;~&#34;</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&gt;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"> &gt;lower $[ day-abbreviations3 [ &gt;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&gt;number ] [ </span></span><span class="line"><span class="cl"> &gt;lower $[ month-abbreviations [ &gt;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">&#34; &#34;</span> split1 <span class="s">&#34; &#34;</span> split1 <span class="s">&#34; &#34;</span> split1 <span class="s">&#34; &#34;</span> split1 <span class="s">&#34; &#34;</span> split1 { </span></span><span class="line"><span class="cl"> [ [ string&gt;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&gt;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&gt;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">&#34;20-30/5 5 */5 * * /path/to/command&#34;</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">&#34;/path/to/command&#34;</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&gt;&gt; :&gt; month </span></span><span class="line"><span class="cl"> cronentry months&gt;&gt; [ month <span class="nb">&gt;= </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&gt;&gt; <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>&gt;&gt;day <span class="m">0 </span>&gt;&gt;hour <span class="m">0 </span>&gt;&gt;minute month&lt;&lt; <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 :&gt; weekday </span></span><span class="line"><span class="cl"> cronentry days-of-week&gt;&gt; [ weekday <span class="nb">&gt;= </span>] <span class="nb">find nip </span>[ </span></span><span class="line"><span class="cl"> cronentry days-of-week&gt;&gt; <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>:&gt; days-to-weekday </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> timestamp day&gt;&gt; :&gt; day </span></span><span class="line"><span class="cl"> cronentry days&gt;&gt; [ day <span class="nb">&gt;= </span>] <span class="nb">find nip </span>[ </span></span><span class="line"><span class="cl"> cronentry days&gt;&gt; <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>:&gt; days-to-day </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> cronentry days-of-week&gt;&gt; <span class="nb">length </span><span class="m">7 </span><span class="nb">= </span></span></span><span class="line"><span class="cl"> cronentry days&gt;&gt; <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>&gt;&gt;hour <span class="m">0 </span>&gt;&gt;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&gt;&gt; :&gt; hour </span></span><span class="line"><span class="cl"> cronentry hours&gt;&gt; [ hour <span class="nb">&gt;= </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&gt;&gt; <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>&gt;&gt;minute hour&lt;&lt; <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&gt;&gt; :&gt; minute </span></span><span class="line"><span class="cl"> cronentry minutes&gt;&gt; [ minute <span class="nb">&gt;= </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&gt;&gt; <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&lt;&lt; <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>&gt;&gt;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">&#34;0 0 29 2 *&#34;</span> now next-time-after timestamp&gt;rfc822 <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;Thu, 29 Feb 2024 00:00:00 -0800&#34;</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">&#34;0 0 29 2 *&#34;</span> now <span class="m">5 </span>[ </span></span><span class="line"><span class="cl"> <span class="nb">dupd </span>next-time-after [ timestamp&gt;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">&#34;Thu, 29 Feb 2024 00:00:00 -0800&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;Tue, 29 Feb 2028 00:00:00 -0800&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;Sun, 29 Feb 2032 00:00:00 -0800&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;Fri, 29 Feb 2036 00:00:00 -0800&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;Wed, 29 Feb 2040 00:00:00 -0800&#34;</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 -0700 https://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 &ldquo;kata&rdquo; and subsequently increasing your degree of proficiency via levels of &ldquo;kyu&rdquo;. 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 &ndash; albeit some of these are in &ldquo;beta&rdquo; status &ndash; 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&rsquo;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 -0700 https://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 &ldquo;special&rdquo; 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&rsquo;s find the special indices &ndash; 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 &ndash; 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&#39;</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 &ldquo;script&rdquo;, 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&gt;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">&#34;%u =&gt; %u\n&#34;</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 -0700 https://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 &ndash; 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">&#34;hangman&#34;</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">&#34;hangman&#34;</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&rsquo;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 &ndash; 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">&#34;https://raw.githubusercontent.com/mrjbq7/re-factor/master/hangman/words.txt&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;vocab:hangman/words.txt&#34;</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">&#34;vocab:hangman/words.txt&#34;</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&amp;&amp; <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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="sc">CHAR: \s </span>&lt;interleaved&gt; <span class="k">; </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>&#39;[ </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 &ldquo;hanged man&rdquo; 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">- &gt;= </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">&#34;Welcome to Hangman!&#34;</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">&#34;Your word is: &#34;</span> <span class="nb">write </span>guessed-word <span class="nb">print </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;Your guesses: &#34;</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">&#34;What is your guess? &#34;</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">&#34;Great guess!&#34;</span> <span class="s">&#34;Sorry, it&#39;s not there.&#34;</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">&#34;Sorry, you lost!&#34;</span> <span class="s">&#34;Congrats! You did it!&#34;</span> <span class="nb">? print </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;Your word was: &#34;</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&rsquo;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">&#34;hangman&#34;</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 -0700 https://re.factorcode.org/2023/11/javascript-arrays.html <p><a href="https://en.wikipedia.org/wiki/JSON">JSON</a> or &ldquo;JavaScript Object Notation&rdquo; 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">&#34;John&#34;</span><span class="p">,</span> <span class="s2">&#34;Doe&#34;</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">&#34;John&#34;</span><span class="p">,</span> <span class="nx">lastName</span><span class="o">:</span> <span class="s2">&#34;Doe&#34;</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&rsquo;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">&#34;John&#34;</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">&#34;Doe&#34;</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&rsquo;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">&#34;firstName&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&#34;John&#34;</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">&#34;lastName&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&#34;Doe&#34;</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">&#34;age&#34;</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 &ldquo;dot notation&rdquo; 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">&#34;John&#34;</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">&#34;Doe&#34;</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&hellip; 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">&gt;</span> <span class="kr">const</span> <span class="nx">list</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;A&#34;</span><span class="p">,</span> <span class="s2">&#34;B&#34;</span><span class="p">,</span> <span class="s2">&#34;C&#34;</span><span class="p">];</span> </span></span><span class="line"><span class="cl"><span class="o">&gt;</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">&gt;</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">&#34;A&#34;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nx">list</span><span class="p">[</span><span class="s2">&#34;0&#34;</span><span class="p">]</span> </span></span><span class="line"><span class="cl"><span class="s2">&#34;A&#34;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nx">list</span><span class="p">.</span><span class="nx">key</span> <span class="o">=</span> <span class="s2">&#34;value&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="o">&gt;</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">&gt;</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">&#34;value&#34;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="o">&gt;</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">&#34;A&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="o">:</span> <span class="s2">&#34;B&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="o">:</span> <span class="s2">&#34;C&#34;</span><span class="p">,</span> <span class="nx">key</span><span class="o">:</span> <span class="s2">&#34;value&#34;</span><span class="p">}</span> </span></span></code></pre></div><p>That&rsquo;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">&lt;js-array&gt;</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&gt;&gt; <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&gt;&gt; <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> &lt;js-array&gt; </span></span><span class="line"><span class="cl"> { <span class="s">&#34;A&#34;</span> <span class="s">&#34;B&#34;</span> <span class="s">&#34;C&#34;</span> } [ <span class="nb">suffix! </span>] <span class="nb">each </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;value&#34;</span> <span class="s">&#34;key&#34;</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">&#34;A&#34;</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">&#34;A&#34;</span> <span class="s">&#34;B&#34;</span> <span class="s">&#34;C&#34;</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 &gt;alist </span><span class="m">. </span></span></span><span class="line"><span class="cl">{ { <span class="s">&#34;key&#34;</span> <span class="s">&#34;value&#34;</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">&#34;key&#34;</span> <span class="nb">of </span><span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;value&#34;</span> </span></span></code></pre></div><p>Well, it doesn&rsquo;t handle converting <a href="https://docs.factorcode.org/content/article-strings.html">string</a> keys so that <code>&quot;0&quot; of</code> would return the same value as <code>first</code>. And it doesn&rsquo;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&rsquo;m not familiar with&hellip;</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 -0700 https://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[&#34;a&#34;] = 1 m[&#34;b&#34;] = 2 m[&#34;sum&#34;] = lambda self: self[&#34;a&#34;] + self[&#34;b&#34;] print(m[&#34;sum&#34;]) </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> &ndash; 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">&lt;magic-dict&gt;</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&gt;&gt; <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&gt;&gt; <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> &lt;magic-dict&gt; </span></span><span class="line"><span class="cl"> <span class="m">1 </span><span class="s">&#34;a&#34;</span> <span class="nb">pick set-at </span></span></span><span class="line"><span class="cl"> <span class="m">2 </span><span class="s">&#34;b&#34;</span> <span class="nb">pick set-at </span></span></span><span class="line"><span class="cl"> [ [ <span class="s">&#34;a&#34;</span> <span class="nb">of </span>] [ <span class="s">&#34;b&#34;</span> <span class="nb">of </span>] <span class="nb">bi + </span>] <span class="s">&#34;sum&#34;</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">&#34;sum&#34;</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 -0700 https://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 &ndash; 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> &ndash; 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&rsquo;s still possible to have the good ol&rsquo; days back &ndash; 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 -0700 https://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> &lt; <span class="nc">integer</span> <span class="m">0 </span><span class="nb">&gt; </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">&#34;object&#34;</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">&#34;positive integer&#34;</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">&#34;positive integer&#34;</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">&#34;object&#34;</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">&#34;object&#34;</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">&gt; </span>] } wat <span class="nb">drop </span><span class="s">&#34;positive integer&#34;</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">&#34;object&#34;</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">&gt; </span>] } wat <span class="nb">drop </span><span class="s">&#34;positive integer&#34;</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">&#34;five&#34;</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">&#34;integer&#34;</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">&#34;maybe a string&#34;</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">&#34;positive integer&#34;</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">&gt;bignum </span>wat <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;five&#34;</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">&#34;integer&#34;</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">&#34;hello&#34;</span> wat <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;maybe a string&#34;</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">&#34;object&#34;</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 -0700 https://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">&#34;std&#34;</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">&lt;</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">&lt;</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">&#34;{} block took {} ms</span><span class="se">\n</span><span class="s">&#34;</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">&gt;</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 &ndash; so slow that it isn&rsquo;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>:&gt; m </span></span><span class="line"><span class="cl"> V{ } <span class="nb">clone </span>:&gt; l </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> now :&gt; start! </span></span><span class="line"><span class="cl"> <span class="m">0 </span> :&gt; 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">&lt; </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">&lt; </span>] [ </span></span><span class="line"><span class="cl"> l <span class="nb">length </span>random :&gt; j </span></span><span class="line"><span class="cl"> j l <span class="nb">nth </span>:&gt; 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&gt;milliseconds </span></span><span class="line"><span class="cl"> <span class="s">&#34;%d block took %d ms\n&#34;</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&rsquo;d like to understand better &ndash; possibly due to excessive rehashing or some allocation pattern with the Factor garbage collector &ndash; 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&rsquo;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 &lt; 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 &lt; 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 &amp; 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 &lt; curr and metadata[idx].isUsed()) or </span></span></span><span class="line"><span class="cl"><span class="gi">+ (idx &gt; 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) &amp; 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 &lt; 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 &#34;std.hash_map repeat fetchRemove&#34; { </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 &#34;std.hash_map rehash&#34; { </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 &lt; 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 &lt; 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 &ndash; and Factor takes only about 50% more time than Zig&rsquo;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&rsquo;s F14 hash table</a>, which doesn&rsquo;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 -0700 https://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&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;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 &ndash; in the spirit of <a href="https://en.wikipedia.org/wiki/Fizz_buzz">Fizz buzz</a> &ndash; called <em>SMAC</em>, which is basically a program that converts the first million numbers into strings or <code>&quot;SMAC&quot;</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">&#34;SMAC&#34;</span> ] [ number&gt;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 &ndash; run in a single-thread &ndash; 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>&lt;n-groups&gt; </span></span><span class="line"><span class="cl"> [ &#39;[ _ [ 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 &lt;threaded-server&gt; </span></span><span class="line"><span class="cl"> <span class="s">&#34;smac&#34;</span> &gt;&gt;name </span></span><span class="line"><span class="cl"> <span class="m">7979 </span>&gt;&gt;insecure </span></span><span class="line"><span class="cl"> <span class="nb">swap </span>&#39;[ _ smac. ] &gt;&gt;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 -0700 https://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 &ldquo;object shape&rdquo; 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">&gt; </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">&gt; </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 &ndash; 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">&#34;The answer is: &#34;</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&rsquo;s a cool syntax word &ndash; and it&rsquo;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 -0700 https://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&rsquo;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 &ndash; checking minimally that it looks like an <code>http</code> or <code>https</code> URL &ndash; 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">&#34;http&#34;</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&gt;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 -0700 https://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">&#34;stars in the Milky Way&#34;</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">&#34;no&#34;</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">&#34;a few&#34;</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">&#34;several&#34;</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">&#34;tens of&#34;</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">&#34;hundreds of&#34;</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">&#34;thousands of&#34;</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">&#34;millions and millions of&#34;</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">&#34;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">.&#34;</span><span class="p">)</span> </span></span></code></pre></div><p>Let&rsquo;s build this functionality in <a href="https://factorcode.org">Factor</a>!</p> <h3 id="range-syntax">Range Syntax</h3> <p>First, let&rsquo;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">&#34;thousands of&#34;</span> ] } </span></span></code></pre></div><p>But that&rsquo;s not that elegant, instead let&rsquo;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">&#34;thousands of&#34;</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&rsquo;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>&#39;[ <span class="nb">dup </span>_ @ ] ] </span></span><span class="line"><span class="cl"> [ <span class="nb">second </span>&#39;[ <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">&#34;no&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">1 </span>..= <span class="m">3 </span>[ <span class="s">&#34;a few&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">4 </span>..= <span class="m">9 </span>[ <span class="s">&#34;several&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">10 </span>..= <span class="m">99 </span>[ <span class="s">&#34;tens of&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">100 </span>..= <span class="m">999 </span>[ <span class="s">&#34;hundreds of&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">1000 </span>..= <span class="m">999,999 </span>[ <span class="s">&#34;thousands of&#34;</span> ] } </span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">&#34;millions and millions of&#34;</span> ] </span></span><span class="line"><span class="cl">} sequence-case <span class="s">&#34;There are %s stars in the Milky Way.\n&#34;</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 -0700 https://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>&lt;iota&gt; <span class="nb">&lt;repetition&gt; </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">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s1">&#39;*&#39;</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">&lt;string&gt; </span>] <span class="nb">map </span><span class="s">&#34;\n&#34;</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>&lt;iota&gt; <span class="nb">&lt;repetition&gt; </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">&lt;string&gt; </span>] <span class="nb">map </span><span class="s">&#34;\n&#34;</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 -0700 https://re.factorcode.org/2023/09/leb128.html <p><a href="https://en.wikipedia.org/wiki/LEB128">LEB128</a> &ndash; Little Endian Base 128 &ndash; 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&gt;</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&gt;</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&gt; ] <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">&gt;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>:&gt; 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">&gt;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>:&gt; 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>:&gt; <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>&gt;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>&gt;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&gt; ] unit-test </span></span><span class="line"><span class="cl">{ B{ <span class="m">0xe5 0x8e 0x26 </span>} } [ <span class="m">624485 </span>&gt;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&gt; ] 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>&gt;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&gt; ] unit-test </span></span><span class="line"><span class="cl">{ B{ <span class="m">0xc0 0xbb 0x78 </span>} } [ <span class="m">-123456 </span>&gt;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&gt; ] 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 &ldquo;Masked VByte&rdquo; and &ldquo;Stream VByte&rdquo; 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 -0700 https://re.factorcode.org/2023/09/periodic-table.html <p>Despite the difficulty that I had with freshman chemistry, I&rsquo;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>&lt;roll-button&gt;</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">&#34;periodic-table&#34;</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 -0700 https://re.factorcode.org/2023/08/rgba-clock.html <p>Today, I&rsquo;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 &ndash; usually seconds &ndash; 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 &ndash; we need a way to convert a timestamp into a <a href="https://docs.factorcode.org/content/word-rgba%2Ccolors.html">rgba color</a> &ndash; 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&gt;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&gt;unix-time <span class="nb">&gt;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 &lt;rgba&gt; <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> &lt; <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&gt;&gt; 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&gt;&gt; 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&gt;&gt; foreground&lt;&lt; ] </span></span><span class="line"><span class="cl"> [ [ &lt;solid&gt; ] <span class="nb">dip </span>interior&lt;&lt; ] <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">&lt;rgba-clock&gt;</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">&#34;99:99:99&#34;</span> rgba-clock new-label </span></span><span class="line"><span class="cl"> monospace-font &gt;&gt;font </span></span><span class="line"><span class="cl"> <span class="nb">dup </span>&#39;[ </span></span><span class="line"><span class="cl"> _ now </span></span><span class="line"><span class="cl"> [ timestamp&gt;hms &gt;&gt;string ] </span></span><span class="line"><span class="cl"> [ timestamp&gt;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 &lt;timer&gt; &gt;&gt;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> &lt;rgba-clock&gt; 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 -0700 https://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 &ndash; by default 9 rows by 17 columns &ndash; 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">&lt;array&gt; </span>] <span class="nb">replicate </span>:&gt; board </span></span><span class="line"><span class="cl"> HEIGHT <span class="nb">2/ </span>:&gt; y! </span></span><span class="line"><span class="cl"> WIDTH <span class="nb">2/ </span>:&gt; 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>:&gt; <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">&lt; </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@%&amp;#/^</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">&#34; .o+=*BOX@%&amp;#/^SE&#34;</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">&#34;fc94b0c1e5b0987c5843997697ee9fb7&#34;</span> </span></span><span class="line"><span class="cl"> hex-string&gt;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 -0700 https://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 &ndash; either incrementing the indices after the &ldquo;max index&rdquo; 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>&#39;[ _ <span class="nb">+ &gt;= </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 &lt;slice-unsafe&gt; ] <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 &ldquo;<em>5 choose 3</em>&rdquo;:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code 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>&lt;iota&gt; <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">&gt;&gt;&gt;&gt;</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">&gt;&gt;&gt;&gt;</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">&gt;&gt;&gt;&gt;</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">&gt;</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">=&gt;</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">&gt;</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">=&gt;</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">&gt;</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">&gt;</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">&gt;</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&gt; <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>&gt; 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">&#34;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)&#34;</span> &gt; 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=&gt;</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=&gt;</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">&#34;Elapsed time: 10685.687736 msecs&#34;</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">&#34;print (1..200).combinations(4).Array.elems&#34;</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 -0700 https://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&rsquo;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 &ndash; <code>&quot;hello&quot;length</code> and <code>&quot;foo&quot;&quot;bar&quot;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&rsquo;s fully-qualified name (e.g., <code>math:+</code> or <code>xml.writer:pprint-xml</code>)</li> <li>The Emacs &ldquo;FUEL&rdquo; 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&amp;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>&hellip;</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 &ldquo;Base 16 encoding&rdquo; 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 &ldquo;Base 32 encoding&rdquo; 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&rsquo; Crockford&rsquo;s Base 32 encoding</a></li> <li><a href="https://docs.factorcode.org/content/vocab-base32hex.html">base32hex</a>: implements &ldquo;Base 32 Encoding with Extended Hex Alphabet&rdquo; 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 &ldquo;<em>better and stronger spiritual successor to BZip2</em>&rdquo;</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 &ldquo;<em>did you mean?</em>&rdquo; restarts when tokens aren&rsquo;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 &ldquo;brute force&rdquo; 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 &ldquo;padded&rdquo; 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&rsquo;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&rsquo;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 &ldquo;wombat&rdquo; 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 &ldquo;Monaco&rdquo; to &ldquo;Menlo&rdquo;</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>&hellip;</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>&lt;script&gt;</code> and <code>&lt;meta&gt;</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>&gt;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&gt;w/w</code>, <code>w&gt;h/h</code>, <code>h&gt;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>&hellip;</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 &ldquo;;&rdquo; 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 -0700 https://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 &ldquo;key&rdquo; 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 &ldquo;LEMON&rdquo;:</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">&#34;ABCDEFGHIJKLMNOPQRSTUVWXYZ&#34;</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 &gt;upper :&gt; MSG </span></span><span class="line"><span class="cl"> key &gt;upper :&gt; 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">&#34;&#34;</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">&gt;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&gt;</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">&#34;VBVTQBYOLCPB&#34;</span> } [ </span></span><span class="line"><span class="cl"> <span class="s">&#34;ATTACKATDAWN&#34;</span> <span class="s">&#34;VICTORY&#34;</span> &gt;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">&#34;ATTACKATDAWN&#34;</span> } [ </span></span><span class="line"><span class="cl"> <span class="s">&#34;VBVTQBYOLCPB&#34;</span> <span class="s">&#34;VICTORY&#34;</span> vigenere&gt; </span></span><span class="line"><span class="cl">] unit-test </span></span></code></pre></div><p>It gained a reputation for being exceptionally strong &ndash; even being described as &ldquo;<em>unbreakable</em>&rdquo; and &ldquo;<em>impossible of translation</em>&rdquo;. 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 &gt;upper :&gt; MSG </span></span><span class="line"><span class="cl"> key &gt;upper &gt;sbuf :&gt; 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">&#34;&#34;</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">&gt;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&gt;</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">&#34;FWGLCDEJIKG&#34;</span> } [ </span></span><span class="line"><span class="cl"> <span class="s">&#34;AWESOMENESS&#34;</span> <span class="s">&#34;FACTOR&#34;</span> &gt;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">&#34;AWESOMENESS&#34;</span> } [ </span></span><span class="line"><span class="cl"> <span class="s">&#34;FWGLCDEJIKG&#34;</span> <span class="s">&#34;FACTOR&#34;</span> autokey&gt; </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 -0700 https://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> &ndash; 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> &ndash; 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 &ldquo;easy help&rdquo; 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">&#34;things&#34;</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">&#34;that other thing&#34;</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">&#34;text&#34;</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">&#34;Hello&#34;</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">&#34;Hello\nHello&#34;</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">&#34;roman&#34;</span> <span class="s">&#34;Roman numerals&#34;</span> </span></span><span class="line"><span class="cl">The { $vocab-link <span class="s">&#34;roman&#34;</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"> &gt;roman </span></span><span class="line"><span class="cl"> &gt;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&gt; } </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&rsquo;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 -0700 https://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 &ldquo;better rainbow method&rdquo; 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 &gt;graphemes :&gt; chars </span></span><span class="line"><span class="cl"> 2pi chars <span class="nb">length / </span>:&gt; 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>&lt;rgba&gt; :&gt; color </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> s <span class="s">&#34;&#34;</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">&gt;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 -0700 https://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 &ldquo;offline Factor documentation&rdquo; 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">&#34;resource:docs&#34;</span> [ <span class="s">&#34;resource:docs.zim&#34;</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&rsquo;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&rsquo;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&rsquo;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 -0700 https://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&amp;hl=en_US&amp;gl=US&amp;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&rsquo;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 &ldquo;Zeno IMproved&rdquo;, 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&rsquo;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&rsquo;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&gt;&gt; <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&gt;&gt; <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&gt;&gt; <span class="nb">seek-absolute seek-input </span></span></span><span class="line"><span class="cl"> entry-count&gt;&gt; [ <span class="m">8 </span><span class="nb">read </span>le&gt; ] <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&gt;&gt; <span class="nb">seek-absolute seek-input </span></span></span><span class="line"><span class="cl"> entry-count&gt;&gt; [ <span class="m">4 </span><span class="nb">read </span>le&gt; ] <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&gt;&gt; <span class="nb">seek-absolute seek-input </span></span></span><span class="line"><span class="cl"> cluster-count&gt;&gt; [ <span class="m">8 </span><span class="nb">read </span>le&gt; ] <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&gt; </span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>le&gt; </span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>le&gt; </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&gt;&gt; <span class="nb">read </span>&gt;&gt;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&gt; </span></span><span class="line"><span class="cl"> <span class="m">4 </span><span class="nb">read </span>le&gt; </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&gt;&gt; <span class="nb">read </span>&gt;&gt;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&gt; <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&gt;&gt; <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 &ldquo;no compression&rdquo; 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&gt; </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&gt; ] <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 &ldquo;ZStandard compression&rdquo; 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 &lt;c-direct-array&gt; ] [ <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">&#34;zlib not supported&#34;</span> <span class="nb">throw </span>] } </span></span><span class="line"><span class="cl"> { <span class="m">3 </span>[ <span class="s">&#34;bzip2 not supported&#34;</span> <span class="nb">throw </span>] } </span></span><span class="line"><span class="cl"> { <span class="m">4 </span>[ <span class="s">&#34;lzma not supported&#34;</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 :&gt; <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>:&gt; zero </span></span><span class="line"><span class="cl"> n offsets <span class="nb">nth </span>:&gt; 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>:&gt; 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&gt;&gt; <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&gt;&gt; </span></span><span class="line"><span class="cl"> entry cluster-number&gt;&gt; </span></span><span class="line"><span class="cl"> zim read-blob-index </span></span><span class="line"><span class="cl"> entry mime-type&gt;&gt; </span></span><span class="line"><span class="cl"> zim mime-types&gt;&gt; <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&gt;&gt; ] [ 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>&#39;[ _ 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 &ldquo;main page&rdquo; 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&gt;&gt; main-page&gt;&gt; ] [ 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>&lt;namespace&gt;&lt;url&gt;</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&gt;&gt; entry-count&gt;&gt; &lt;iota&gt; [ </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&gt;&gt; &lt;=&gt; </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&gt;&gt; &lt;=&gt; ] <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&gt;&gt; <span class="nb">namespace = </span>] [ url&gt;&gt; url <span class="nb">= </span>] </span></span><span class="line"><span class="cl"> } 1&amp;&amp; [ <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&rsquo;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>&#39;[ _ 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&rsquo;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">&lt;zim-responder&gt;</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">&gt; </span>] [ <span class="nb">first length </span><span class="m">1 </span><span class="nb">= </span>] } 1&amp;&amp; </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">&#34;/&#34;</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&gt;&gt; <span class="nb">dup </span>path&gt;&gt; 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"> &lt;content&gt; binary &gt;&gt;content-encoding </span></span><span class="line"><span class="cl"> ] [ </span></span><span class="line"><span class="cl"> <span class="nb">2drop </span>&lt;404&gt; </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 &ndash; like searching URLs, titles, and content, or dealing with split ZIM files (when over 4GB on file systems like FAT32) &ndash; 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 -0700 https://re.factorcode.org/2023/05/calendar-ranges.html <p>A post recently titled <a href="https://martinheinz.dev/blog/96">Python&rsquo;s Missing Batteries: Essential Libraries You&rsquo;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&rsquo;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>&lt;date-utc&gt; </span></span><span class="line"><span class="cl"> <span class="m">2023 4 30 </span>&lt;date-utc&gt; </span></span><span class="line"><span class="cl"> <span class="m">2 </span>days &lt;timestamp-range&gt; [ <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>&lt;timestamp-range&gt;</code> work the same way as <code>&lt;range&gt;</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 -0700 https://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"> &gt;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">&#34; -_.&#34;</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&#39;</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&#39;</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&rsquo;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">&gt;camelcase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str&#39;</span> <span class="nf">) </span>[ &gt;lower ] [ &gt;title ] <span class="s">&#34;&#34;</span> case2 <span class="k">; </span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">&gt;pascalcase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str&#39;</span> <span class="nf">) </span>[ &gt;title ] <span class="s">&#34;&#34;</span> case1 <span class="k">; </span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">&gt;snakecase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str&#39;</span> <span class="nf">) </span>[ &gt;lower ] <span class="s">&#34;_&#34;</span> case1 <span class="k">; </span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">&gt;adacase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str&#39;</span> <span class="nf">) </span>[ &gt;title ] <span class="s">&#34;_&#34;</span> case1 <span class="k">; </span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">&gt;macrocase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str&#39;</span> <span class="nf">) </span>[ &gt;upper ] <span class="s">&#34;_&#34;</span> case1 <span class="k">; </span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">&gt;kebabcase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str&#39;</span> <span class="nf">) </span>[ &gt;lower ] <span class="s">&#34;-&#34;</span> case1 <span class="k">; </span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">&gt;traincase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str&#39;</span> <span class="nf">) </span>[ &gt;title ] <span class="s">&#34;-&#34;</span> case1 <span class="k">; </span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">&gt;cobolcase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str&#39;</span> <span class="nf">) </span>[ &gt;upper ] <span class="s">&#34;-&#34;</span> case1 <span class="k">; </span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">&gt;lowercase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str&#39;</span> <span class="nf">) </span>[ &gt;lower ] <span class="s">&#34; &#34;</span> case1 <span class="k">; </span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">&gt;uppercase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str&#39;</span> <span class="nf">) </span>[ &gt;upper ] <span class="s">&#34; &#34;</span> case1 <span class="k">; </span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">&gt;titlecase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str&#39;</span> <span class="nf">) </span>[ &gt;title ] <span class="s">&#34; &#34;</span> case1 <span class="k">; </span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">&gt;sentencecase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str&#39;</span> <span class="nf">) </span>[ &gt;title ] [ &gt;lower ] <span class="s">&#34; &#34;</span> case2 <span class="k">; </span></span></span><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">&gt;dotcase</span> <span class="nf">( </span><span class="nv">str</span> <span class="nf">-- </span><span class="nv">str&#39;</span> <span class="nf">) </span>[ &gt;lower ] <span class="s">&#34;.&#34;</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 -0700 https://re.factorcode.org/2023/05/unicode.html <p>The <a href="https://rust-lang.org">Rust programming language</a> is pretty cool. I&rsquo;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">&#34;नमस्ते&#34;</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">&#34;नमस्ते&#34;</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">&#34;नमस्ते&#34;</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">&#34;न&#34;</span> <span class="s">&#34;म&#34;</span> <span class="s">&#34;स&#34;</span> <span class="s">&#34;्&#34;</span> <span class="s">&#34;त&#34;</span> <span class="s">&#34;े&#34;</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">&#34;नमस्ते&#34;</span> <span class="nb">&gt;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">&#34;नमस्ते&#34;</span> [ char&gt;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">&#34;devanagari-letter-na&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;devanagari-letter-ma&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;devanagari-letter-sa&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;devanagari-sign-virama&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;devanagari-letter-ta&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;devanagari-vowel-sign-e&#34;</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">&#34;नमस्ते&#34;</span> &gt;graphemes [ <span class="nb">&gt;string </span>] <span class="nb">map </span><span class="m">. </span></span></span><span class="line"><span class="cl">{ <span class="s">&#34;न&#34;</span> <span class="s">&#34;म&#34;</span> <span class="s">&#34;स्&#34;</span> <span class="s">&#34;ते&#34;</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">&#34;नमस्ते&#34;</span> &gt;graphemes [ <span class="nb">&gt;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 -0700 https://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 &ldquo;color score&rdquo; to the user showing how well they matched it. Some users have even <a href="https://github.com/search?q=rgbullseye&amp;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">&gt;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> &lt; <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&gt;&gt; <span class="nb">first </span>children&gt;&gt; <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 &ldquo;color score&rdquo;</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">&lt;match-button&gt;</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">&#34;Match Color&#34;</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&gt;&gt; compute-model ] <span class="nb">bi@ </span></span></span><span class="line"><span class="cl"> color-score <span class="s">&#34;Your score: %d&#34;</span> sprintf </span></span><span class="line"><span class="cl"> <span class="nb">over </span>children&gt;&gt; <span class="nb">first </span>text&lt;&lt; relayout </span></span><span class="line"><span class="cl"> ] &lt;border-button&gt; <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">&lt;reset-button&gt;</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">&#34;Random&#34;</span> [ </span></span><span class="line"><span class="cl"> find-color-previews <span class="nb">drop </span>model&gt;&gt; </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"> ] &lt;border-button&gt; <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">&lt;color-picker-game&gt;</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>} &gt;&gt;gap </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> random-color &lt;model&gt; :&gt; left-model </span></span><span class="line"><span class="cl"> <span class="no">\ &lt;rgba&gt;</span> &lt;color-sliders&gt; :&gt; <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 &lt;track&gt; </span></span><span class="line"><span class="cl"> left-model &lt;color-preview&gt; 1/2 track-add </span></span><span class="line"><span class="cl"> right-model &lt;color-preview&gt; 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 &lt;color-status&gt; <span class="no">f </span>track-add </span></span><span class="line"><span class="cl"> &lt;match-button&gt; <span class="no">f </span>track-add </span></span><span class="line"><span class="cl"> &lt;reset-button&gt; <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">&#34;Color Picker Game&#34;</span> } } </span></span><span class="line"><span class="cl"> &lt;color-picker-game&gt; &gt;&gt;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 -0800 https://re.factorcode.org/2023/04/openai.html <p>It&rsquo;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">&#34;sk-.....................&#34;</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&hellip;</p> <div class="highlight"><pre tabindex="0" class="chroma"><code 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;what is the factor programming language&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;text-davinci-003&#34;</span> &lt;completion&gt; </span></span><span class="line"><span class="cl"> <span class="m">100 </span>&gt;&gt;max_tokens create-completion </span></span><span class="line"><span class="cl"> <span class="s">&#34;choices&#34;</span> <span class="nb">of first </span><span class="s">&#34;text&#34;</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 -0700 https://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">&#34;NUL Null char&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;SOH Start of Heading&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;STX Start of Text&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;ETX End of Text&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;EOT End of Transmission&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;ENQ Enquiry&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;ACK Acknowledgment&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;BEL Bell&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;BS Back Space&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;HT Horizontal Tab&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;LF Line Feed&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;VT Vertical Tab&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;FF Form Feed&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;CR Carriage Return&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;SO Shift Out / X-On&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;SI Shift In / X-Off&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;DLE Data Line Escape&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;DC1 Device Control 1 (oft. XON)&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;DC2 Device Control 2&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;DC3 Device Control 3 (oft. XOFF)&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;DC4 Device Control 4&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;NAK Negative Acknowledgement&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;SYN Synchronous Idle&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;ETB End of Transmit Block&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;CAN Cancel&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;EM End of Medium&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;SUB Substitute&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;ESC Escape&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;FS File Separator&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;GS Group Separator&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;RS Record Separator&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;US Unit Separator&#34;</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">&#34;ASCII Control Characters - 0 to 31&#34;</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"> [ &gt;dec ] </span></span><span class="line"><span class="cl"> [ &gt;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"> [ &gt;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"> [ &gt;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">&#34; &#34;</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">&#34;DEC&#34;</span> <span class="s">&#34;OCT&#34;</span> <span class="s">&#34;HEX&#34;</span> <span class="s">&#34;BIN&#34;</span> <span class="s">&#34;Symbol&#34;</span> <span class="s">&#34;Description&#34;</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">&#34;monospace&#34;</span> } } [ ascii. ] with-style </span></span><span class="line"><span class="cl"> ] with-pdf-writer pdf&gt;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 -0800 https://re.factorcode.org/2023/03/shortuuid.html <p>The <a href="https://github.com/skorokithakis/shortuuid">shortuuid</a> project is a &ldquo;<em>simple python library that generates concise, unambiguous, URL-safe UUIDs</em>&rdquo;. I thought it would be a fun exercise to implement this in <a href="https://factorcode.org">Factor</a>.</p> <p>What is a &ldquo;short UUID&rdquo;?</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 &ldquo;<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>&rdquo;. 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">&#34;23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz&#34;</span> </span></span></code></pre></div><p>We encode a numeric input by repeatedly &ldquo;divmod&rdquo;, 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">&gt; </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">&#34;&#34;</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 -0700 https://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 &ndash; 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&rsquo;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 &ndash; 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>&lt;iota&gt; [ </span></span><span class="line"><span class="cl"> number&gt;string </span></span><span class="line"><span class="cl"> <span class="s">&#34;vocab:geo-tz/zoom&#34;</span> <span class="s">&#34;.dat&#34;</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 &ndash; 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">&#34;vocab:geo-tz/leaves.dat&#34;</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&gt;&gt; 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&gt;&gt; ] [ leaf idx0&gt;&gt; ] <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>:&gt; 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&rsquo;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&gt;&gt; tile-key &gt;=&lt; ] search <span class="nb">swap </span>[ </span></span><span class="line"><span class="cl"> <span class="nb">dup </span>key&gt;&gt; tile-key <span class="nb">= </span>[ </span></span><span class="line"><span class="cl"> idx&gt;&gt; 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>:&gt; 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>&lt;iota&gt; [| 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">&gt;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&rsquo;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">&#34;America/Los_Angeles&#34;</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">&#34;Australia/Sydney&#34;</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 -0700 https://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&rsquo;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">&lt;netinet/in.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;sys/socket.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;unistd.h&gt;</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">&amp;</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">&amp;</span><span class="n">address</span><span class="p">,</span> <span class="o">&amp;</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">&#34;%s</span><span class="se">\n</span><span class="s">&#34;</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">&#34;hello world</span><span class="se">\n</span><span class="s">&#34;</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 &ndash; without any error checking, like in the original example &ndash; 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>&lt;byte-array&gt; :&gt; buffer </span></span><span class="line"><span class="cl"> AF_INET SOCK_STREAM <span class="m">0 </span>socket :&gt; server </span></span><span class="line"><span class="cl"> sockaddr-in malloc-struct </span></span><span class="line"><span class="cl"> AF_INET &gt;&gt;family </span></span><span class="line"><span class="cl"> <span class="m">0 </span>&gt;&gt;addr </span></span><span class="line"><span class="cl"> <span class="m">15000 </span>htons &gt;&gt;port :&gt; 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 &lt;ref&gt; accept :&gt; 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">&#34;hello world\n&#34;</span> &gt;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> &ndash; gaining the benefit of working on Windows as well as error handling and logging &ndash; 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 &lt;threaded-server&gt; </span></span><span class="line"><span class="cl"> <span class="m">15000 </span>&gt;&gt;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">&#34;hello world\n&#34;</span> &gt;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"> ] &gt;&gt;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 -0700 https://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 &gt;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">&#34;A&#34;</span> <span class="m">1 </span>} { <span class="s">&#34;B&#34;</span> <span class="m">2 </span>} { <span class="s">&#34;C&#34;</span> <span class="m">3 </span>} { <span class="s">&#34;D&#34;</span> <span class="m">4 </span>} } </span></span></code></pre></div><p>And it seems to work &ndash; 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">&#34;C&#34;</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">&#34;C&#34;</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">&#34;D&#34;</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">&#34;B&#34;</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">&#34;A&#34;</span> <span class="m">998403 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;B&#34;</span> <span class="m">2000400 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;C&#34;</span> <span class="m">3001528 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;D&#34;</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&rsquo;s not <em>that</em> fast&hellip; 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 &gt;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"> &#39;[ _ _ 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">&#34;A&#34;</span> <span class="m">1000088 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;B&#34;</span> <span class="m">1999445 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;C&#34;</span> <span class="m">3000688 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;D&#34;</span> <span class="m">3999779 </span>} </span></span><span class="line"><span class="cl">} </span></span></code></pre></div><p>That&rsquo;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">&lt;vose&gt;</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>:&gt; small </span></span><span class="line"><span class="cl"> V{ } <span class="nb">clone </span>:&gt; large </span></span><span class="line"><span class="cl"> dist <span class="nb">assoc-size </span>:&gt; n </span></span><span class="line"><span class="cl"> n <span class="no">f </span><span class="nb">&lt;array&gt; </span>:&gt; 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 :&gt; <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">&lt; </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>:&gt; s </span></span><span class="line"><span class="cl"> large <span class="nb">pop </span>:&gt; 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">&lt; </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 &ndash; choosing a random item index, check it&rsquo;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&gt;&gt; rnd random* </span></span><span class="line"><span class="cl"> <span class="nb">dup </span>obj probs&gt;&gt; <span class="nb">nth </span>rnd (random-unit) <span class="nb">&gt;= </span></span></span><span class="line"><span class="cl"> [ obj alias&gt;&gt; <span class="nb">nth </span>] <span class="nb">unless </span></span></span><span class="line"><span class="cl"> obj items&gt;&gt; <span class="nb">nth </span><span class="k">; </span></span></span></code></pre></div><p>It&rsquo;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 &lt;vose&gt; 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 -0700 https://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&amp;format=json&amp;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&#34; http://api.duckduckgo.com&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">swap </span><span class="s">&#34;q&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> <span class="s">&#34;json&#34;</span> <span class="s">&#34;format&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> <span class="s">&#34;1&#34;</span> <span class="s">&#34;pretty&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> <span class="s">&#34;1&#34;</span> <span class="s">&#34;no_redirect&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> <span class="s">&#34;1&#34;</span> <span class="s">&#34;no_html&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> <span class="s">&#34;1&#34;</span> <span class="s">&#34;skip_disambig&#34;</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&gt; <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">&#34;Heading&#34;</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">&#34;AbstractURL&#34;</span> <span class="nb">of </span>&gt;url write-object <span class="nb">nl </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;AbstractText&#34;</span> <span class="nb">of print </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;AbstractSource&#34;</span> <span class="nb">of </span><span class="s">&#34;- &#34;</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">&#34;Result&#34;</span> <span class="nb">of </span>[ </span></span><span class="line"><span class="cl"> <span class="s">&#34;&lt;a href=\&#34;&#34;</span> ?head <span class="nb">drop </span><span class="s">&#34;\&#34;&gt;&#34;</span> split1 <span class="s">&#34;&lt;/a&gt;&#34;</span> split1 </span></span><span class="line"><span class="cl"> [ <span class="nb">swap </span>&gt;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">&#34;Results&#34;</span> <span class="nb">of </span>[ result. ] <span class="nb">each </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;RelatedTopics&#34;</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">&#34;factorcode&#34;</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 -0800 https://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">&#34;magic&#34;</span> { </span></span><span class="line"><span class="cl"> { [ os macosx? ] [ <span class="s">&#34;libmagic.dylib&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ os unix? ] [ <span class="s">&#34;libmagic.so&#34;</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>&ldquo;returns a textual description of the contents of the filename argument&rdquo;</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&hellip;</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 &amp;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">&#34;vm/factor.hpp&#34;</span> guess-file <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;C++ source, ASCII text&#34;</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">&#34;Factor.app/Contents/Info.plist&#34;</span> guess-file <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;XML 1.0 document, Unicode text, UTF-8 text&#34;</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">&#34;factor&#34;</span> guess-file <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;symbolic link to Factor.app/Contents/MacOS/factor&#34;</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">&#34;factor.image&#34;</span> guess-file <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;data&#34;</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 -0800 https://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 &ldquo;full documentation and a working demo&rdquo; 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 &ndash; and wonder what kind of a language could run <a href="https://spot.colorado.edu/~sniderc/poetry/wakawaka.html">Waka Waka Bang Splat</a> &ndash; 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&rsquo;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> &ndash; 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">&#34;The&#34;</span> octet octet octet <span class="no">f </span></span></span><span class="line"><span class="cl"> octet <span class="s">&#34;in the&#34;</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">&#34;127.0.0.1&#34;</span> &gt;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">&#34;2001:db8:3333:4444:5555:6666:7777:8888&#34;</span> &gt;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 -0800 https://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 &ldquo;state of the art and missed opportunities&rdquo; 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 &ldquo;PRO-nouncable QUINT-uplets&rdquo;.</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 &ndash; 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">&#34;bdfghjklmnprstvz&#34;</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">&#34;aiou&#34;</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">&gt;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">&#34;&#34;</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">&gt;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 &gt;quint16 ] <span class="nb">bi@ </span><span class="s">&#34;-&#34;</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&gt;</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 &ndash; 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 &gt;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&gt;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 &gt;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&gt;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&gt; 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">&#34;127.0.0.1&#34;</span> <span class="s">&#34;lusab-babad&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;63.84.220.193&#34;</span> <span class="s">&#34;gutih-tugad&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;63.118.7.35&#34;</span> <span class="s">&#34;gutuk-bisog&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;140.98.193.141&#34;</span> <span class="s">&#34;mudof-sakat&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;64.255.6.200&#34;</span> <span class="s">&#34;haguz-biram&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;128.30.52.45&#34;</span> <span class="s">&#34;mabiv-gibot&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;147.67.119.2&#34;</span> <span class="s">&#34;natag-lisaf&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;212.58.253.68&#34;</span> <span class="s">&#34;tibup-zujah&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;216.35.68.215&#34;</span> <span class="s">&#34;tobog-higil&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;216.68.232.21&#34;</span> <span class="s">&#34;todah-vobij&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;198.81.129.136&#34;</span> <span class="s">&#34;sinid-makam&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;12.110.110.204&#34;</span> <span class="s">&#34;budov-kuras&#34;</span> } </span></span><span class="line"><span class="cl"> } [ </span></span><span class="line"><span class="cl"> [ quint&gt;ipv4 <span class="nb">= </span>] [ <span class="nb">swap </span>ipv4&gt;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 -0800 https://re.factorcode.org/2023/01/semantic-versioning.html <p><a href="https://semver.org/">Semantic Versioning</a> (or &ldquo;semver&rdquo; 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 &ldquo;pre-release&rdquo; and &ldquo;build&rdquo; 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 &ldquo;semver&rdquo;, 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&rsquo;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">&#34;0.99.0&#34;</span> &gt;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">&#34;0.99.0&#34;</span> &gt;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">&#34;0.99.0&#34;</span> <span class="s">&#34;0.100.0&#34;</span> semver&lt;=&gt; <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">&#34;0.100.0-joke+haha&#34;</span> &gt;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 &ldquo;semver ranges&rdquo; to manage project dependencies.</p> Five Questions https://re.factorcode.org/2023/01/five-questions.html Sat, 21 Jan 2023 11:00:00 -0800 https://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&rsquo;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 &ndash; 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> &ndash; 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>&lt;date&gt; days-since <span class="nb">&gt;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>:&gt; n </span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>n <span class="nb">&lt; </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&#39;</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>&#39;[ </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">&#34;a&#34;</span> <span class="m">1 </span><span class="s">&#34;b&#34;</span> <span class="m">2 </span><span class="s">&#34;c&#34;</span> <span class="m">3 </span>} } [ </span></span><span class="line"><span class="cl"> { <span class="s">&#34;a&#34;</span> <span class="s">&#34;b&#34;</span> <span class="s">&#34;c&#34;</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">&#34;a&#34;</span> <span class="m">1 </span><span class="s">&#34;b&#34;</span> <span class="m">2 </span><span class="s">&#34;c&#34;</span> <span class="m">3 </span>} } [ </span></span><span class="line"><span class="cl"> { <span class="s">&#34;a&#34;</span> <span class="s">&#34;b&#34;</span> <span class="s">&#34;c&#34;</span> <span class="s">&#34;d&#34;</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&rsquo;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&gt;string ] <span class="nb">map </span></span></span><span class="line"><span class="cl"> [ <span class="nb">concat </span>string&gt;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, &hellip;, 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 &ndash; 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&rsquo;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>&lt;groups&gt; [ <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 &ndash; 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"> [ &gt;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">&#34; &#34;</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 -0800 https://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>&ldquo;Dutch government was forced to release the source code of their DigiD digital authentication iOS app&rdquo;</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">&lt;= </span>] [ <span class="nb">drop </span><span class="s">&#34;⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪&#34;</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">&#34;🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪&#34;</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">&#34;🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪&#34;</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">&#34;🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪&#34;</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">&#34;🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪&#34;</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">&#34;🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪&#34;</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">&#34;🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪&#34;</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">&#34;🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪&#34;</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">&#34;🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪&#34;</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">&#34;🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪&#34;</span> ] } </span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">&#34;🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵&#34;</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">&lt;= </span>] [ <span class="s">&#34;⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="m">0.1 </span><span class="nb">&lt;= </span>] [ <span class="s">&#34;🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="m">0.2 </span><span class="nb">&lt;= </span>] [ <span class="s">&#34;🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="m">0.3 </span><span class="nb">&lt;= </span>] [ <span class="s">&#34;🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="m">0.4 </span><span class="nb">&lt;= </span>] [ <span class="s">&#34;🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="m">0.5 </span><span class="nb">&lt;= </span>] [ <span class="s">&#34;🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="m">0.6 </span><span class="nb">&lt;= </span>] [ <span class="s">&#34;🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="m">0.7 </span><span class="nb">&lt;= </span>] [ <span class="s">&#34;🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="m">0.8 </span><span class="nb">&lt;= </span>] [ <span class="s">&#34;🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="m">0.9 </span><span class="nb">&lt;= </span>] [ <span class="s">&#34;🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪&#34;</span> ] } </span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">&#34;🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵&#34;</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 &ndash; 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 - &gt;integer dup </span><span class="m">10 </span><span class="nb">+ </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪&#34;</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 &ndash; 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">&gt;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&rsquo;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 -0800 https://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&rsquo;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">&#34;back&#34;</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">&#34;Go back to the previous Gemini URL.&#34;</span> } </span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">&#34;b&#34;</span> } } } </span></span><span class="line"><span class="cl"> T{ command </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;forward&#34;</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">&#34;Go forward to the next Gemini URL.&#34;</span> } </span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">&#34;f&#34;</span> } } } </span></span><span class="line"><span class="cl"> T{ command </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;history&#34;</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">&#34;Display recently viewed Gemini URLs.&#34;</span> } </span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">&#34;h&#34;</span> <span class="s">&#34;hist&#34;</span> } } } </span></span><span class="line"><span class="cl"> T{ command </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;less&#34;</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">&#34;View the most recent Gemini URL in a pager.&#34;</span> } </span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">&#34;l&#34;</span> } } } </span></span><span class="line"><span class="cl"> T{ command </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;ls&#34;</span> } </span></span><span class="line"><span class="cl"> { quot [ gemini-ls ] } </span></span><span class="line"><span class="cl"> { help <span class="s">&#34;List the currently available links.&#34;</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">&#34;go&#34;</span> } </span></span><span class="line"><span class="cl"> { quot [ gemini-go ] } </span></span><span class="line"><span class="cl"> { help <span class="s">&#34;Go to a Gemini URL&#34;</span> } </span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">&#34;g&#34;</span> } } } </span></span><span class="line"><span class="cl"> T{ command </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;gus&#34;</span> } </span></span><span class="line"><span class="cl"> { quot [ <span class="nb">drop </span><span class="s">&#34;gemini://gus.guru/search&#34;</span> gemini-go ] } </span></span><span class="line"><span class="cl"> { help <span class="s">&#34;Submit a query to the GUS search engine.&#34;</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">&#34;up&#34;</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">&#34;Go up one directory from the recent Gemini URL.&#34;</span> } </span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">&#34;u&#34;</span> } } } </span></span><span class="line"><span class="cl"> T{ command </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;url&#34;</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">&#34;Print the most recent Gemini URL.&#34;</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">&#34;reload&#34;</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">&#34;Reload the most recent Gemini URL.&#34;</span> } </span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">&#34;r&#34;</span> } } } </span></span><span class="line"><span class="cl"> T{ command </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;root&#34;</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">&#34;Navigate to the most recent Gemini URL&#39;s root.&#34;</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">&#34;shell&#34;</span> } </span></span><span class="line"><span class="cl"> { quot [ gemini-shell ] } </span></span><span class="line"><span class="cl"> { help <span class="s">&#34;&#39;cat&#39; the most recent Gemini URL through a shell.&#34;</span> } </span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">&#34;!&#34;</span> } } } </span></span><span class="line"><span class="cl"> T{ command </span></span><span class="line"><span class="cl"> { name <span class="s">&#34;quit&#34;</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">&#34;Quit the program.&#34;</span> } </span></span><span class="line"><span class="cl"> { abbrevs { <span class="s">&#34;q&#34;</span> <span class="s">&#34;exit&#34;</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 &ldquo;missing command&rdquo;:</p> <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> &lt; <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&gt;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">&#34;Welcome to Project Gemini!&#34;</span> <span class="s">&#34;GEMINI&gt;&#34;</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&gt; 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 -0800 https://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">&#34;tour&#34;</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 -0800 https://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 &ndash; and this is where much of Tomasz&rsquo; 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=&#34;1 2 + .&#34; 3 $ cat foo.factor USE: io &#34;Hello World&#34; print 12 $ ./factor foo.factor Hello World --- Data stack: 12 </code></pre><p>Previously, the first example would error with a &ldquo;No word named “+” found in current vocabulary search path&rdquo; and the second example would complain that the &ldquo;Quotation&rsquo;s stack effect does not match call site&rdquo; 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 -0700 https://re.factorcode.org/2018/07/factor-0-98-now-available.html <p><em>“Even though you&rsquo;re growing up, you should never stop having fun.” - Nina Dobrev</em></p> <p>I&rsquo;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 &ldquo;out of memory&rdquo; 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&rsquo;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&rsquo;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>&lt;iota&gt;</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 &ldquo;executable&rdquo;</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 &ldquo;triple-quote&rdquo; 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&rsquo;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 &ldquo;flex hex&rdquo; 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&rsquo;s to text (<code>&quot;I :heart: Factor :+1:&quot;</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 &ldquo;uʍop ǝpᴉsdn&rdquo; 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&rsquo;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 &ldquo;frozen&rdquo; sequence</li> <li><a href="https://docs.factorcode.org/content/vocab-sequences.interleaved.html">sequences.interleaved</a>: virtual &ldquo;interleaved&rdquo; 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 &ldquo;Text to PDF&rdquo; utility</li> <li><a href="https://docs.factorcode.org/content/vocab-text-to-speech.html">text-to-speech</a>: cross-platform &ldquo;Text to Speech&rdquo; utility</li> <li><a href="https://docs.factorcode.org/content/vocab-tools.cal.html">tools.cal</a>: command-line &ldquo;cal&rdquo; tool</li> <li><a href="https://docs.factorcode.org/content/vocab-tools.cat.html">tools.cat</a>: command-line &ldquo;cat&rdquo; tool</li> <li><a href="https://docs.factorcode.org/content/vocab-tools.copy.html">tools.copy</a>: command-line &ldquo;copy&rdquo; tool</li> <li><a href="https://docs.factorcode.org/content/vocab-tools.echo.html">tools.echo</a>: command-line&quot;echo&quot; tool</li> <li><a href="https://docs.factorcode.org/content/vocab-tools.grep.html">tools.grep</a>: command-line&quot;grep&quot; 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 &ldquo;move&rdquo; tool</li> <li><a href="https://docs.factorcode.org/content/vocab-tools.seq.html">tools.seq</a>: command-line &ldquo;seq&rdquo; tool</li> <li><a href="https://docs.factorcode.org/content/vocab-tools.tree.html">tools.tree</a>: command-line &ldquo;tree&rdquo; tool</li> <li><a href="https://docs.factorcode.org/content/vocab-tools.uniq.html">tools.uniq</a>: command-line &ldquo;uniq&rdquo; tool</li> <li><a href="https://docs.factorcode.org/content/vocab-tools.wc.html">tools.wc</a>: command-line &ldquo;wc&rdquo; 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 &ldquo;file drop&rdquo; 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 &ldquo;space&rdquo; 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&rsquo;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&gt;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 &ldquo;Lua-style&rdquo; 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 -0800 https://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&rsquo;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">&#34;minesweeper&#34;</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 &ldquo;cells&rdquo;.</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 &ldquo;state&rdquo; 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"> &#39;[ _ [ 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?&gt;&gt; ] [ <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>&gt;&gt;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?&gt;&gt; ] [ <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 &ldquo;adjacent mines&rdquo; 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&lt;&lt; </span></span><span class="line"><span class="cl"> ] each-cell cells <span class="k">; </span></span></span></code></pre></div><p>Since we aren&rsquo;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?&gt;&gt; ] <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&rsquo;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&gt;&gt; +clicked+ <span class="nb">= </span>] [ mined?&gt;&gt; ] } 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&rsquo;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&gt;&gt; +clicked+ <span class="nb">= </span>] [ mined?&gt;&gt; ] } 1&amp;&amp; ] <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&gt;&gt; +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 &ldquo;clickable&rdquo; (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>:&gt; <span class="nf">( </span><span class="nv">row&#39;</span> <span class="nv">col&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> cells row&#39; col&#39; cell-at [ </span></span><span class="line"><span class="cl"> mined?&gt;&gt; [ </span></span><span class="line"><span class="cl"> cells row&#39; col&#39; 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&rsquo;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&#39;t be a mine</span> </span></span><span class="line"><span class="cl"> <span class="nb">dup </span>mined?&gt;&gt; [ </span></span><span class="line"><span class="cl"> cells unmined-cell <span class="no">t </span>&gt;&gt;mined? <span class="nb">drop </span><span class="no">f </span>&gt;&gt;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&gt;&gt; { +clicked+ +flagged+ } <span class="nb">member? </span>[ <span class="nb">drop </span>] [ </span></span><span class="line"><span class="cl"> +clicked+ &gt;&gt;state </span></span><span class="line"><span class="cl"> { [ mined?&gt;&gt; <span class="nb">not </span>] [ #adjacent&gt;&gt; <span class="m">0 </span><span class="nb">= </span>] } 1&amp;&amp; [ </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&gt;&gt; { </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>&gt;&gt;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> &lt; <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">&lt;grid-gadget&gt;</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 &gt;&gt;cells </span></span><span class="line"><span class="cl"> H{ } <span class="nb">clone </span>&gt;&gt;textures </span></span><span class="line"><span class="cl"> COLOR: gray &lt;solid&gt; &gt;&gt;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&gt;&gt; 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?&gt;&gt; <span class="nb">and </span>[ </span></span><span class="line"><span class="cl"> cell state&gt;&gt; +clicked+ <span class="nb">= </span><span class="s">&#34;mineclicked.gif&#34;</span> <span class="s">&#34;mine.gif&#34;</span> <span class="nb">? </span></span></span><span class="line"><span class="cl"> ] [ </span></span><span class="line"><span class="cl"> cell state&gt; </span></span><span class="line"><span class="cl"> { </span></span><span class="line"><span class="cl"> { +question+ [ <span class="s">&#34;question.gif&#34;</span> ] } </span></span><span class="line"><span class="cl"> { +flagged+ [ game-over? <span class="s">&#34;misflagged.gif&#34;</span> <span class="s">&#34;flagged.gif&#34;</span> <span class="nb">? </span>] } </span></span><span class="line"><span class="cl"> { +clicked+ [ </span></span><span class="line"><span class="cl"> cell mined?&gt;&gt; [ </span></span><span class="line"><span class="cl"> <span class="s">&#34;mine.gif&#34;</span> </span></span><span class="line"><span class="cl"> ] [ </span></span><span class="line"><span class="cl"> cell #adjacent&gt;&gt; <span class="m">0 </span><span class="nb">or </span>number&gt;string </span></span><span class="line"><span class="cl"> <span class="s">&#34;open&#34;</span> <span class="s">&#34;.gif&#34;</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">&#34;blank.gif&#34;</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">&#34;vocab:minesweeper/_resources/&#34;</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&gt;&gt; [ load-image { <span class="m">0 0 </span>} &lt;texture&gt; ] <span class="nb">cache </span></span></span><span class="line"><span class="cl"> [ dim&gt;&gt; ] [ 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&gt;&gt; game-over? :&gt; game-over? </span></span><span class="line"><span class="cl"> gadget cells&gt;&gt; [| 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>:&gt; <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>:&gt; <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&gt;&gt; :&gt; 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>:&gt; <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>:&gt; <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&gt;&gt; :&gt; 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 &gt;&gt;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">&#34;1&#34;</span> } } [ com-easy ] } </span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">&#34;2&#34;</span> } } [ com-medium ] } </span></span><span class="line"><span class="cl"> { T{ key-down { sym <span class="s">&#34;3&#34;</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">&#34; &#34;</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">&#34;Minesweeper&#34;</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>&lt;grid-gadget&gt; &gt;&gt;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 &ldquo;uh-oh!&rdquo; 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 -0800 https://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&rsquo;ll be solving this exactly, using integer &ldquo;numbers of cents&rdquo;, 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>&lt;iota&gt; amb-lazy :&gt; w </span></span><span class="line"><span class="cl"> <span class="m">711 </span>w <span class="nb">- </span>&lt;iota&gt; amb-lazy :&gt; x </span></span><span class="line"><span class="cl"> <span class="m">711 </span>w <span class="nb">- </span>x <span class="nb">- </span>&lt;iota&gt; amb-lazy :&gt; 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>:&gt; 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 -0800 https://re.factorcode.org/2017/02/dirty-money-code-challenge.html <p>There&rsquo;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">&#34;,&#34;</span> <span class="s">&#34;.&#34;</span> replace string&gt;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&gt; :&gt; data </span></span><span class="line"><span class="cl"> data <span class="s">&#34;content&#34;</span> <span class="nb">of </span>dollars </span></span><span class="line"><span class="cl"> data <span class="s">&#34;links&#34;</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&rsquo;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 -0800 https://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">&#34;first&#34;</span> <span class="s">&#34;a partridge in a pear tree&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;second&#34;</span> <span class="s">&#34;two turtle doves and &#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;third&#34;</span> <span class="s">&#34;three French hens, &#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;fourth&#34;</span> <span class="s">&#34;four calling birds, &#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;fifth&#34;</span> <span class="s">&#34;five golden rings, &#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;sixth&#34;</span> <span class="s">&#34;six geese a-laying, &#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;seventh&#34;</span> <span class="s">&#34;seven swans a-swimming, &#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;eighth&#34;</span> <span class="s">&#34;eight maids a-milking, &#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;ninth&#34;</span> <span class="s">&#34;nine ladies dancing, &#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;tenth&#34;</span> <span class="s">&#34;ten lords a-leaping, &#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;eleventh&#34;</span> <span class="s">&#34;eleven pipers piping, &#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;twelfth&#34;</span> <span class="s">&#34;twelve drummers drumming, &#34;</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">&#34;On the %s day of Christmas my true love gave to me %s.&#34;</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 -0800 https://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 &ldquo;colored dot&rdquo; 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">&#34;tell application \&#34;AnyBar\&#34; to set image name to \&#34;blue\&#34;&#34;</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 &#34;blue&#34; | 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">&#34;blue&#34;</span> &gt;byte-array <span class="s">&#34;127.0.0.1&#34;</span> <span class="m">1738 </span>&lt;inet4&gt; 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">&#34;localhost&#34;</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 -0800 https://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 &ldquo;reverse factorial&rdquo; 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 &gt; </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 -0700 https://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> &lt; <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">&#34;1&#34;</span> </span></span><span class="line"><span class="cl"> ] [ </span></span><span class="line"><span class="cl"> name&gt;&gt; mime-type { </span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="s">&#34;text/&#34;</span> <span class="nb">head? </span>] [ <span class="nb">drop </span><span class="s">&#34;0&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="s">&#34;image/gif&#34;</span> <span class="nb">= </span>] [ <span class="nb">drop </span><span class="s">&#34;g&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="s">&#34;image/&#34;</span> <span class="nb">head? </span>] [ <span class="nb">drop </span><span class="s">&#34;I&#34;</span> ] } </span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">&#34;9&#34;</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&gt;&gt; ] <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&gt;&gt; ?head <span class="nb">drop </span></span></span><span class="line"><span class="cl"> server serving-hostname&gt; </span></span><span class="line"><span class="cl"> server insecure&gt; </span></span><span class="line"><span class="cl"> <span class="s">&#34;%s%s\t%s\t%s\t%d\r\n&#34;</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">&#34;\t\r\n&#34;</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&gt;&gt; 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">&lt;gopher-server&gt;</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">&#34;gopher.server&#34;</span> &gt;&gt;name </span></span><span class="line"><span class="cl"> <span class="nb">swap </span>&gt;&gt;insecure </span></span><span class="line"><span class="cl"> binary &gt;&gt;encoding </span></span><span class="line"><span class="cl"> <span class="s">&#34;localhost&#34;</span> &gt;&gt;serving-hostname </span></span><span class="line"><span class="cl"> <span class="nb">swap </span>resolve-symlinks &gt;&gt;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"> &lt;gopher-server&gt; 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 -0700 https://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">&gt; </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">&lt;cuckoo-buckets&gt;</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">&lt;array&gt; </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&#39;</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>&lt;displaced-alien&gt; [ 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">&lt;cuckoo-filter&gt;</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"> &lt;cuckoo-buckets&gt; 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 &ldquo;kickdown&rdquo; 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 :&gt; <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&gt;&gt; :&gt; buckets </span></span><span class="line"><span class="cl"> buckets <span class="nb">length </span>:&gt; 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>:&gt; 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 &gt;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 :&gt; <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&gt;&gt; :&gt; buckets </span></span><span class="line"><span class="cl"> buckets <span class="nb">length </span>:&gt; 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 :&gt; <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&gt;&gt; :&gt; buckets </span></span><span class="line"><span class="cl"> buckets <span class="nb">length </span>:&gt; 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 -0700 https://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">&gt;&gt;&gt;</span> <span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s2">&#34;ls -l&#34;</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 &ldquo;backticks&rdquo;:</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">&gt;</span> <span class="nb">system</span><span class="p">(</span><span class="s2">&#34;ls -l&#34;</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">&gt;</span> <span class="sb">`ls -l`</span> </span></span></code></pre></div><p>Basically, the difference between &ldquo;system&rdquo; and &ldquo;backticks&rdquo; is:</p> <ul> <li>&ldquo;system&rdquo; executes a command, returning the exit code of the process.</li> <li>&ldquo;backticks&rdquo; 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 &ldquo;backticks&rdquo;, 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">&#34;`&#34;</span> parse-multiline-string &#39;[ </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 -0700 https://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">&#34;12:00&#34;</span> clock-angle ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">60 </span>} [ <span class="s">&#34;2:00&#34;</span> clock-angle ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">180 </span>} [ <span class="s">&#34;6:00&#34;</span> clock-angle ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">18 </span>} [ <span class="s">&#34;5:24&#34;</span> clock-angle ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">50 </span>} [ <span class="s">&#34;2:20&#34;</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">&#34;:&#34;</span> split1 [ number&gt;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 -0700 https://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&rsquo;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">&amp;&amp;</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">&#39; &#39;</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">&lt;</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">&lt;repetition&gt; 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">&#34;hello&#34;</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">&#34;hello&#34;</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">&#34;hello&#34;</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">&#34;hhhhhhello&#34;</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 -0700 https://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&gt;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 -0700 https://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&rsquo;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 &ldquo;ay&rdquo; 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">&#34;igpay&#34;</span> } [ <span class="s">&#34;pig&#34;</span> pig-latin ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;ananabay&#34;</span> } [ <span class="s">&#34;banana&#34;</span> pig-latin ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;ashtray&#34;</span> } [ <span class="s">&#34;trash&#34;</span> pig-latin ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;appyhay&#34;</span> } [ <span class="s">&#34;happy&#34;</span> pig-latin ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;uckday&#34;</span> } [ <span class="s">&#34;duck&#34;</span> pig-latin ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;oveglay&#34;</span> } [ <span class="s">&#34;glove&#34;</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 &ldquo;way&rdquo; 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">&#34;eggway&#34;</span> } [ <span class="s">&#34;egg&#34;</span> pig-latin ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;inboxway&#34;</span> } [ <span class="s">&#34;inbox&#34;</span> pig-latin ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;eightway&#34;</span> } [ <span class="s">&#34;eight&#34;</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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[ <span class="s">&#34;aeiou&#34;</span> <span class="nb">member? </span>] <span class="nb">find drop </span>[ </span></span><span class="line"><span class="cl"> <span class="s">&#34;way&#34;</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">&#34;ay&#34;</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 &ldquo;yay&rdquo; (or &ldquo;i&rdquo;) instead of &ldquo;way&rdquo;</li> <li>different rules like adding &ldquo;ag&rdquo; before each vowel (&ldquo;pagig lagatagin&rdquo;)</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 -0700 https://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 &ldquo;golf&rdquo; 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 &ldquo;normal&rdquo; 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 &ldquo;un-slicing&rdquo; 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&gt;&gt; ] [ to&gt;&gt; <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">&#34;---------------------&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">11 </span>} [ <span class="s">&#34;------------------X1-&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">12 </span>} [ <span class="s">&#34;----------------X1-&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">15 </span>} [ <span class="s">&#34;------------------5/5&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">20 </span>} [ <span class="s">&#34;11111111111111111111&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">20 </span>} [ <span class="s">&#34;5/5-----------------&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">20 </span>} [ <span class="s">&#34;------------------5/X&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">40 </span>} [ <span class="s">&#34;X5/5----------------&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">80 </span>} [ <span class="s">&#34;-8-7714215X6172183-&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">83 </span>} [ <span class="s">&#34;12X4--3-69/-98/8-8-&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">150 </span>} [ <span class="s">&#34;5/5/5/5/5/5/5/5/5/5/5&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">144 </span>} [ <span class="s">&#34;XXX6-3/819-44X6-&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">266 </span>} [ <span class="s">&#34;XXXXXXXXX81-&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">271 </span>} [ <span class="s">&#34;XXXXXXXXX9/2&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">279 </span>} [ <span class="s">&#34;XXXXXXXXXX33&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">295 </span>} [ <span class="s">&#34;XXXXXXXXXXX5&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">300 </span>} [ <span class="s">&#34;XXXXXXXXXXXX&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">100 </span>} [ <span class="s">&#34;-/-/-/-/-/-/-/-/-/-/-&#34;</span> bowl ] unit-test </span></span><span class="line"><span class="cl">{ <span class="m">190 </span>} [ <span class="s">&#34;9/9/9/9/9/9/9/9/9/9/9&#34;</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 -0700 https://re.factorcode.org/2015/08/haikunator.html <p>The <a href="https://github.com/usmanbashir/haikunator">Haikunator</a> is a project to provide &ldquo;Heroku-like memorable random names&rdquo;. 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 &ldquo;arrays of strings&rdquo; 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">&#34;0123456789&#34;</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">&#34;&#34;</span> <span class="nb">replicate-as </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;%s-%s-%s&#34;</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">&#34;odd-water-8344&#34;</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">&#34;flat-tooth-9324&#34;</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">&#34;wandering-lion-8346&#34;</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">&#34;yellow-mud-9780&#34;</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">&#34;patient-unit-4203&#34;</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">&#34;floral-feather-1023&#34;</span> </span></span></code></pre></div><p>Some versions of &ldquo;haikunate&rdquo; 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 -0700 https://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">&#34;/&#34;</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">&#34;https://imgur.com/random&#34;</span> scrape-html <span class="nb">nip </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;image_src&#34;</span> <span class="s">&#34;rel&#34;</span> find-by-attribute-key-value </span></span><span class="line"><span class="cl"> <span class="nb">first </span><span class="s">&#34;href&#34;</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">&#34;https://dynamic.xkcd.com/random/comic/&#34;</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">&gt;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">&#34;https://wallpaperstock.net/random-wallpapers.html&#34;</span> </span></span><span class="line"><span class="cl"> scrape-html <span class="nb">nip </span><span class="s">&#34;wallpaper_thumb&#34;</span> find-by-class-between </span></span><span class="line"><span class="cl"> <span class="s">&#34;a&#34;</span> find-by-name <span class="nb">nip </span><span class="s">&#34;href&#34;</span> attribute </span></span><span class="line"><span class="cl"> <span class="s">&#34;https://wallpaperstock.net&#34;</span> <span class="nb">prepend </span>scrape-html <span class="nb">nip </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;the_view_link&#34;</span> find-by-id <span class="nb">nip </span><span class="s">&#34;href&#34;</span> attribute </span></span><span class="line"><span class="cl"> <span class="s">&#34;https:&#34;</span> <span class="nb">prepend </span>scrape-html <span class="nb">nip </span><span class="s">&#34;myImage&#34;</span> find-by-id <span class="nb">nip </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;src&#34;</span> attribute <span class="s">&#34;https:&#34;</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 -0700 https://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">&#34;osascript&#34;</span> <span class="s">&#34;-e&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;tell app \&#34;Finder\&#34; to get posix path of (get desktop picture as alias)&#34;</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">&#34;tell application \&#34;Finder\&#34; to set desktop picture to POSIX file \&#34;%s\&#34;&#34;</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 &lt;c-array&gt; [ </span></span><span class="line"><span class="cl"> <span class="m">0 </span>SystemParametersInfo win32-error&lt;&gt;0 </span></span><span class="line"><span class="cl"> ] <span class="nb">keep </span>alien&gt;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&lt;&gt;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">&#34;gsettings&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;get&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;org.gnome.desktop.background&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;picture-uri&#34;</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">&#34;&#39;file://&#34;</span> ?head <span class="nb">drop </span><span class="s">&#34;&#39;&#34;</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">&#34;gsettings&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;set&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;org.gnome.desktop.background&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;picture-uri&#34;</span> </span></span><span class="line"><span class="cl"> } <span class="nb">swap </span>absolute-path <span class="s">&#34;file://&#34;</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 -0700 https://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&rsquo;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&#39;</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&#39;</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">&#34;Could not be simplified to a Constant.&#34;</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">&#34;x&#34;</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&rsquo;s cool, but wouldn&rsquo;t it be better if we could work on <a href="https://docs.factorcode.org/content/word-quotation,quotations.html">quotations</a> directly? Let&rsquo;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">&gt;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>] [ &#39;[ _ Var <span class="nb">boa </span>] ] } </span></span><span class="line"><span class="cl"> { [ <span class="nb">dup integer? </span>] [ &#39;[ _ 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">&#34;x&#34;</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>] &gt;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 -0700 https://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">&gt;= </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&gt;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&gt;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"> &#39;[ _ _ <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 -0700 https://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 &ldquo;2-3-5-7&rdquo; <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 &ldquo;2-3-5&rdquo; 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 &ldquo;<code>{ 2 3 5 } product</code>&rdquo; 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 :&gt; <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 :&gt; <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>&lt;range&gt; [ sieve unmark ] <span class="nb">each </span><span class="k">; </span></span></span></code></pre></div><p>That&rsquo;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>:&gt; 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>:&gt; upper </span></span><span class="line"><span class="cl"> <span class="m">3 </span>upper sqrt <span class="m">2 </span>&lt;range&gt; [| 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 -0700 https://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&rsquo;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 &ldquo;Version 3&rdquo;. In this case, we want to ignore all multiples of the first four prime numbers.</p> <p>To calculate the &ldquo;2-3-5-7&rdquo; 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 &ldquo;<code>{ 2 3 5 7 } product</code>&rdquo; 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 &#39;[ <span class="nb">dup </span>_ <span class="nb">&lt;= </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>&lt;range&gt; [| 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 &lt;bit-array&gt; :&gt; 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>:&gt; 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">&lt;= </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">&gt;fixnum </span>&#39;[ <span class="nb">dup </span>_ <span class="nb">&lt;= </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 :&gt; step </span></span><span class="line"><span class="cl"> i i fixnum*fast upto <span class="nb">&gt;fixnum </span>&#39;[ <span class="nb">dup </span>_ <span class="nb">&lt;= </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 &lt;bit-array&gt; :&gt; 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>:&gt; 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">&lt;= </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 -0700 https://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 &gt; 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">&lt;array&gt; </span>:&gt; 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 &lt;range&gt; [| 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>&lt;bit-array&gt; :&gt; 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 &lt;range&gt; [| 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>&lt;bit-array&gt; :&gt; 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>&lt;range&gt; [| 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>&lt;range&gt; [| 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>:&gt; 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 -0700 https://re.factorcode.org/2015/06/send-more-money.html <p>There&rsquo;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&rsquo;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&rsquo;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&rsquo;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">&gt;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&rsquo;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 :&gt; s </span></span><span class="line"><span class="cl"> digits { s } diff amb-lazy :&gt; e </span></span><span class="line"><span class="cl"> digits { s e } diff amb-lazy :&gt; n </span></span><span class="line"><span class="cl"> digits { s e n } diff amb-lazy :&gt; d </span></span><span class="line"><span class="cl"> { s e n d } &gt;number :&gt; 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 :&gt; m </span></span><span class="line"><span class="cl"> digits { s e n d m } diff amb-lazy :&gt; o </span></span><span class="line"><span class="cl"> digits { s e n d m o } diff amb-lazy :&gt; r </span></span><span class="line"><span class="cl"> { m o r e } &gt;number :&gt; 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 :&gt; y </span></span><span class="line"><span class="cl"> { m o n e y } &gt;number :&gt; 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">&#34; %s\n+ %s\n= %s\n&#34;</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&rsquo;s the answer? Let&rsquo;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&rsquo;s fast too &ndash; 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 -0700 https://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&gt;&gt; <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&rsquo;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">&amp;</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">&#34;test&#34;</span> &gt; /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 -0700 https://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 -0700 https://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 &#39;EOF&#39; 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">&#39;</span><span class="se">\0</span><span class="s1">&#39;</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">&#39;&#39;</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&#39;</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 &#39;EOF&#39; 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">&#39;&#39;</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">&#39;</span><span class="se">\0</span><span class="s1">&#39;</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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>[ <span class="s">&#34;&#34;</span> <span class="nb">&lt;array&gt; </span>] <span class="nb">keep </span>] <span class="nb">keep </span></span></span><span class="line"><span class="cl"> &#39;[ _ [ <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">&#39;&#39;</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&#39;</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">&#39;&#39;</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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ <span class="nb">length </span>] [ <span class="nb">&lt;enum&gt; </span>sort-values ] <span class="nb">bi </span></span></span><span class="line"><span class="cl"> &#39;[ _ <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 -0700 https://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 &ldquo;reverse&rdquo; 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 &ldquo;running status&rdquo; 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 &ldquo;running status&rdquo;.</p> <div class="highlight"><pre 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">&lt; </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">&#34;channel&#34;</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&gt;&gt; write-number ] [ value&gt;&gt; ] [ name&gt;&gt; ] <span class="nb">tri </span>{ </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> { <span class="s">&#34;note-off&#34;</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">&#34;note&#34;</span> <span class="nb">of write1 </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;velocity&#34;</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">&#34;note-on&#34;</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">&#34;note&#34;</span> <span class="nb">of write1 </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;velocity&#34;</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">&#34;polytouch&#34;</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">&#34;note&#34;</span> <span class="nb">of write1 </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;value&#34;</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">&#34;control-change&#34;</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">&#34;control&#34;</span> <span class="nb">of write1 </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;value&#34;</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">&#34;program-change&#34;</span> [ </span></span><span class="line"><span class="cl"> <span class="m">0xc0 </span>[ <span class="s">&#34;program&#34;</span> <span class="nb">of write1 </span>] write-channel ] } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;aftertouch&#34;</span> [ </span></span><span class="line"><span class="cl"> <span class="m">0xd0 </span>[ <span class="s">&#34;value&#34;</span> <span class="nb">of write1 </span>] write-channel ] } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;pitchwheel&#34;</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">&#34;pitch&#34;</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">&#34;sysex&#34;</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">&#34;quarter-made&#34;</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">&#34;frame-type&#34;</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">&#34;frame-value&#34;</span> <span class="nb">of + </span>] <span class="nb">bi write1 </span>] } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;songpos&#34;</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">&#34;song-select&#34;</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">&#34;tune-request&#34;</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">&#34;clock&#34;</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">&#34;start&#34;</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">&#34;continue&#34;</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">&#34;stop&#34;</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">&#34;active-sensing&#34;</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">&#34;reset&#34;</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&gt;&gt; write-number ] [ value&gt;&gt; ] [ name&gt;&gt; ] <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">&#34;sequence-number&#34;</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>&gt;be <span class="nb">write </span>] } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;text&#34;</span> [ <span class="m">0x01 </span><span class="nb">write1 </span>write-string ] } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;copyright&#34;</span> [ <span class="m">0x02 </span><span class="nb">write1 </span>write-string ] } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;track-name&#34;</span> [ <span class="m">0x03 </span><span class="nb">write1 </span>write-string ] } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;instrument-name&#34;</span> [ <span class="m">0x04 </span><span class="nb">write1 </span>write-string ] } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;lyrics&#34;</span> [ <span class="m">0x05 </span><span class="nb">write1 </span>write-string ] } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;marker&#34;</span> [ <span class="m">0x06 </span><span class="nb">write1 </span>write-string ] } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;cue-point&#34;</span> [ <span class="m">0x07 </span><span class="nb">write1 </span>write-string ] } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;device-name&#34;</span> [ <span class="m">0x09 </span><span class="nb">write1 </span>write-string ] } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;channel-prefix&#34;</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">&#34;midi-port&#34;</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">&#34;end-of-track&#34;</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">&#34;set-tempo&#34;</span> [ B{ <span class="m">0x51 0x03 </span>} <span class="nb">write </span><span class="m">3 </span>&gt;be <span class="nb">write </span>] } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;smpte-offset&#34;</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">&#34;frame-rate&#34;</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">&#34;hours&#34;</span> <span class="nb">of + write1 </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;minutes&#34;</span> <span class="nb">of write1 </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;seconds&#34;</span> <span class="nb">of write1 </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;frames&#34;</span> <span class="nb">of write1 </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;subframes&#34;</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">&#34;time-signature&#34;</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">&#34;numerator&#34;</span> <span class="nb">of write1 </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;denominator&#34;</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">&#34;clocks-per-tick&#34;</span> <span class="nb">of write1 </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;notated-32nd-notes-per-beat&#34;</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">&#34;key-signature&#34;</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">&#34;sequencer-specific&#34;</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&gt;&gt; write-number ] </span></span><span class="line"><span class="cl"> [ type&gt;&gt; <span class="nb">write1 </span>] </span></span><span class="line"><span class="cl"> [ bytes&gt;&gt; <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 &ldquo;chunks&rdquo;:</p> <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">&#34;MThd&#34;</span> &gt;byte-array ] <span class="nb">write </span></span></span><span class="line"><span class="cl"> $[ <span class="m">6 4 </span>&gt;be ] <span class="nb">write </span></span></span><span class="line"><span class="cl"> [ format&gt;&gt; ] [ #chunks&gt;&gt; ] [ division&gt;&gt; ] <span class="nb">tri </span></span></span><span class="line"><span class="cl"> [ <span class="m">2 </span>&gt;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">&#34;MTrk&#34;</span> &gt;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&gt;&gt; <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>&gt;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&gt;&gt; write-chunk ] </span></span><span class="line"><span class="cl"> [ chunks&gt;&gt; [ 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&gt;</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&gt;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 -0700 https://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">&lt;midi-event&gt;</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">&lt; </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">&#34;channel&#34;</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">&#34;note-off&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">read1 </span><span class="s">&#34;note&#34;</span> ,, <span class="nb">read1 </span><span class="s">&#34;velocity&#34;</span> ,, ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x90 </span>[ <span class="s">&#34;note-on&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">read1 </span><span class="s">&#34;note&#34;</span> ,, <span class="nb">read1 </span><span class="s">&#34;velocity&#34;</span> ,, ] } </span></span><span class="line"><span class="cl"> { <span class="m">0xa0 </span>[ <span class="s">&#34;polytouch&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">read1 </span><span class="s">&#34;note&#34;</span> ,, <span class="nb">read1 </span><span class="s">&#34;value&#34;</span> ,, ] } </span></span><span class="line"><span class="cl"> { <span class="m">0xb0 </span>[ <span class="s">&#34;control-change&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">read1 </span><span class="s">&#34;control&#34;</span> ,, <span class="nb">read1 </span><span class="s">&#34;value&#34;</span> ,, ] } </span></span><span class="line"><span class="cl"> { <span class="m">0xc0 </span>[ <span class="s">&#34;program-change&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">read1 </span><span class="s">&#34;program&#34;</span> ,, ] } </span></span><span class="line"><span class="cl"> { <span class="m">0xd0 </span>[ <span class="s">&#34;aftertouch&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">read1 </span><span class="s">&#34;value&#34;</span> ,, ] } </span></span><span class="line"><span class="cl"> { <span class="m">0xe0 </span>[ <span class="s">&#34;pitchwheel&#34;</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">&#34;pitch&#34;</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">&#34;sysex&#34;</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">&#34;quarter-made&#34;</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">&#34;frame-type&#34;</span> ,, ] </span></span><span class="line"><span class="cl"> [ <span class="m">0x0f </span><span class="nb">bitand </span><span class="s">&#34;frame-value&#34;</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">&#34;songpos&#34;</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">&#34;song-select&#34;</span> <span class="nb">read1 </span>] } </span></span><span class="line"><span class="cl"> { <span class="m">0xf6 </span>[ <span class="s">&#34;tune-request&#34;</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">&#34;clock&#34;</span> <span class="no">f </span>] } </span></span><span class="line"><span class="cl"> { <span class="m">0xfa </span>[ <span class="s">&#34;start&#34;</span> <span class="no">f </span>] } </span></span><span class="line"><span class="cl"> { <span class="m">0xfb </span>[ <span class="s">&#34;continue&#34;</span> <span class="no">f </span>] } </span></span><span class="line"><span class="cl"> { <span class="m">0xfc </span>[ <span class="s">&#34;stop&#34;</span> <span class="no">f </span>] } </span></span><span class="line"><span class="cl"> { <span class="m">0xfe </span>[ <span class="s">&#34;active-sensing&#34;</span> <span class="no">f </span>] } </span></span><span class="line"><span class="cl"> { <span class="m">0xff </span>[ <span class="s">&#34;reset&#34;</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>&lt;midi-event&gt; <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">&lt;meta-event&gt;</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&gt; <span class="s">&#34;sequence-number&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x01 </span>[ utf8 decode <span class="s">&#34;text&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x02 </span>[ utf8 decode <span class="s">&#34;copyright&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x03 </span>[ utf8 decode <span class="s">&#34;track-name&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x04 </span>[ utf8 decode <span class="s">&#34;instrument-name&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x05 </span>[ utf8 decode <span class="s">&#34;lyrics&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x06 </span>[ utf8 decode <span class="s">&#34;marker&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x07 </span>[ utf8 decode <span class="s">&#34;cue-point&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x09 </span>[ utf8 decode <span class="s">&#34;device-name&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x20 </span>[ <span class="nb">first </span><span class="s">&#34;channel-prefix&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x21 </span>[ <span class="nb">first </span><span class="s">&#34;midi-port&#34;</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">&#34;end-of-track&#34;</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&gt; <span class="s">&#34;set-tempo&#34;</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">&#34;frame-rate&#34;</span> ,, ] </span></span><span class="line"><span class="cl"> [ <span class="m">0x3f </span><span class="nb">bitand </span><span class="s">&#34;hours&#34;</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">&#34;minutes&#34;</span> ,, ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;seconds&#34;</span> ,, ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;frames&#34;</span> ,, ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;subframes&#34;</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">&#34;smpte-offset&#34;</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">&#34;numerator&#34;</span> ,, ] </span></span><span class="line"><span class="cl"> [ <span class="m">2 </span><span class="nb">* </span><span class="s">&#34;denominator&#34;</span> ,, ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;clocks-per-tick&#34;</span> ,, ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;notated-32nd-notes-per-beat&#34;</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">&#34;time-signature&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x59 </span>[ <span class="s">&#34;key-signature&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x7f </span>[ <span class="s">&#34;sequencer-specific&#34;</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 &lt;meta-event&gt; <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">&lt;sysex-event&gt;</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>&lt;sysex-event&gt; <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 &ldquo;running&rdquo;, 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&#39;</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">&lt; </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 &ldquo;running&rdquo;).</p> <div class="highlight"><pre 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&#39;</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 &lt;byte-reader&gt; &lt;peek-stream&gt; [ </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">&lt;midi-header&gt;</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&gt; ] <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">&lt;midi-track&gt;</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 &ldquo;chunk type&rdquo;:</p> <div class="highlight"><pre 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&gt; <span class="nb">read swap </span>{ </span></span><span class="line"><span class="cl"> { $[ <span class="s">&#34;MThd&#34;</span> &gt;byte-array ] [ &lt;midi-header&gt; ] } </span></span><span class="line"><span class="cl"> { $[ <span class="s">&#34;MTrk&#34;</span> &gt;byte-array ] [ &lt;midi-track&gt; ] } </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">&lt;midi&gt;</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&gt;&gt; [ 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 &lt;midi&gt; <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">&gt;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&gt;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 -0700 https://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"> &lt;head-request&gt; <span class="m">0 </span>&gt;&gt;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 &lt;= </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">&#34;location&#34;</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 &ldquo;long URL&rdquo;, 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">&#34;https://bit.ly/1J0vm1x&#34;</span> long-url <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;https://factorcode.org/&#34;</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 -0700 https://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">&#34;World&#34;</span> <span class="s">&#34;name&#34;</span> <span class="nb">set </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;Hello, ${name}&#34;</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">&#34;Mr.&#34;</span> <span class="s">&#34;Anderson&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Hello, ${1} ${0}&#34;</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">&#34;James&#34;</span> <span class="s">&#34;Bond&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;${0}, ${1} ${0}&#34;</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">&#34;Roses&#34;</span> <span class="s">&#34;red&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;${} are ${}&#34;</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">&#34;Factor&#34;</span> <span class="s">&#34;lang&#34;</span> <span class="nb">set </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;cool&#34;</span> <span class="s">&#34;${lang} is ${0}!&#34;</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&rsquo;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 -0800 https://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&rsquo;s recent changes to Internet regulations. Normally, I wouldn&rsquo;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">&#34;https://publicpolicy.verizon.com/blog/entry/fccs-throwback-thursday-move-imposes-1930s-rules-on-the-internet&#34;</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">&#34;blog&#34;</span> find-by-class-between </span></span><span class="line"><span class="cl"><span class="s">&#34;p&#34;</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">&#34;&amp;nbsp;&amp;nbsp;&#34;</span> split-subseq </span></span></code></pre></div><p>4. Parse each word&rsquo;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&gt; ] <span class="nb">map </span><span class="s">&#34; &#34;</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&#39;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&#39;s actions as misguided. the fcc&#39;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&#39;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 -0800 https://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">&gt;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&gt;</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">&#34;&#34;</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 -0800 https://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&rsquo;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">&#34;/&#34;</span> split1 <span class="s">&#34;&#34;</span> <span class="nb">or nip write </span><span class="s">&#34;\r\n&#34;</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"> &gt;url { </span></span><span class="line"><span class="cl"> [ host&gt;&gt; ] </span></span><span class="line"><span class="cl"> [ port&gt;&gt; <span class="m">70 </span><span class="nb">or </span>&lt;inet&gt; ascii ] </span></span><span class="line"><span class="cl"> [ path&gt;&gt; <span class="nb">rest </span>] </span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span>&#39;[ _ 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&rsquo;t have any special handling for menus, or support for a query string that would allow using Gopher &ldquo;search servers&rdquo;. 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&rsquo;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">&#34;gopher://gopher.floodgap.com/1&#34;</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&rsquo;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 -0800 https://re.factorcode.org/2014/12/binary-puzzle.html <p>I&rsquo;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&rsquo;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&rsquo;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">&#34;&#34;&#34; </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">&#34;&#34;&#34;</span> </span></span><span class="line"><span class="cl">[ blank? ] split-when <span class="nb">harvest </span>[ bin&gt; ] <span class="s">&#34;&#34;</span> <span class="nb">map-as </span><span class="m">. </span></span></span></code></pre></div><p>It&rsquo;s a neat message, but I won&rsquo;t spoil the answer for you.</p> Heaps https://re.factorcode.org/2014/12/heaps.html Tue, 02 Dec 2014 15:50:00 -0800 https://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&rsquo;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 &ldquo;heapq&rdquo; 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 -0800 https://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, &ldquo;as tightly as possible&rdquo; 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&rsquo;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 -0800 https://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 -0800 https://re.factorcode.org/2014/11/factor-0-97-now-available.html <p><em>&ldquo;If birds can glide for long periods of time, then&hellip; why can’t I?&rdquo; - Orville Wright</em></p> <p>I&rsquo;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>&rsquo;s.</li> </ul> <p>Some of the improvements to FUEL, Factor&rsquo;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&rsquo;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&gt;</code> and <code>signed-be&gt;</code> and <code>signed&gt;</code></li> <li><a href="https://docs.factorcode.org/content/vocab-io.binary.fast.html">io.binary.fast</a>: generic <code>be&gt;</code> and <code>le&gt;</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>&quot;4ci&quot;</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&rsquo;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 -0700 https://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&gt;number { </span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">30000 </span><span class="nb">&gt; </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">&gt; </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">&gt; </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 &amp;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">&#34;wb&#34;</span> fopen &amp;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 &ldquo;curl&rdquo;:</p> <div class="highlight"><pre 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">&#34;https://factorcode.org&#34;</span> <span class="s">&#34;/tmp/factor.html&#34;</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 -0700 https://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 &ldquo;simple quicksort algorithm&rdquo; has the following pseudocode:</p> <pre tabindex="0"><code>function quicksort(array) less, equal, greater := three empty arrays if length(array) &gt; 1 pivot := select any element of array for each x in array if x &lt; pivot then add x to less if x = pivot then add x to equal if x &gt; 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">&gt; </span>[ </span></span><span class="line"><span class="cl"> V{ } <span class="nb">clone </span>:&gt; less </span></span><span class="line"><span class="cl"> V{ } <span class="nb">clone </span>:&gt; equal </span></span><span class="line"><span class="cl"> V{ } <span class="nb">clone </span>:&gt; greater </span></span><span class="line"><span class="cl"> seq <span class="nb">first </span>:&gt; pivot </span></span><span class="line"><span class="cl"> seq [| x | </span></span><span class="line"><span class="cl"> x pivot &lt;=&gt; { </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"> &#39;[ _ 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 &ldquo;better quicksort algorithm&rdquo; 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) &gt; 1 pivot := select any element of array left := first index of array right := last index of array while left ≤ right while array[left] &lt; pivot left := left + 1 while array[right] &gt; 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">&lt; </span>[ </span></span><span class="line"><span class="cl"> from to <span class="nb">+ 2/ </span>seq nth-unsafe :&gt; pivot </span></span><span class="line"><span class="cl"> from :&gt; left! </span></span><span class="line"><span class="cl"> to :&gt; right! </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> [ left right <span class="nb">&lt;= </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">&lt;= </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 -0700 https://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">&#34;https://worldcup.sfg.io/matches&#34;</span> http-get <span class="nb">nip </span>json </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;status&#34;</span> <span class="nb">of </span><span class="s">&#34;completed&#34;</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">&#34;home_team&#34;</span> <span class="nb">of </span>] [ <span class="s">&#34;away_team&#34;</span> <span class="nb">of </span>] <span class="nb">bi </span></span></span><span class="line"><span class="cl"> [ [ <span class="s">&#34;country&#34;</span> <span class="nb">of </span>] [ <span class="s">&#34;goals&#34;</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">&#34;%s %s x %s %s\n&#34;</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&rsquo;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&rsquo;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">&#34;https://worldcup.sfg.io/matches&#34;</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&gt;&gt; ] [ away_team&gt;&gt; ] [ winner&gt;&gt; ] <span class="nb">tri </span></span></span><span class="line"><span class="cl"> :&gt; <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">&#34;country&#34;</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">&#34;goals&#34;</span> <span class="nb">of </span>number&gt;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">&#34; x &#34;</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">&#34;country&#34;</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">&#34;goals&#34;</span> <span class="nb">of </span>number&gt;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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ status&gt;&gt; <span class="s">&#34;completed&#34;</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 -0700 https://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&rsquo;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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ <span class="s">&#34;/\\?*:|\&#34;&lt;&gt;&#34;</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&#39;</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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ blank? ] split-when <span class="nb">harvest </span><span class="s">&#34; &#34;</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 &ldquo;<code>file</code>&rdquo; 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>&gt;upper { </span></span><span class="line"><span class="cl"> <span class="s">&#34;CON&#34;</span> <span class="s">&#34;PRN&#34;</span> <span class="s">&#34;AUX&#34;</span> <span class="s">&#34;NUL&#34;</span> <span class="s">&#34;COM1&#34;</span> <span class="s">&#34;COM2&#34;</span> <span class="s">&#34;COM3&#34;</span> <span class="s">&#34;COM4&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;COM5&#34;</span> <span class="s">&#34;COM6&#34;</span> <span class="s">&#34;COM7&#34;</span> <span class="s">&#34;COM8&#34;</span> <span class="s">&#34;COM9&#34;</span> <span class="s">&#34;LPT1&#34;</span> <span class="s">&#34;LPT2&#34;</span> <span class="s">&#34;LPT3&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;LPT4&#34;</span> <span class="s">&#34;LPT5&#34;</span> <span class="s">&#34;LPT6&#34;</span> <span class="s">&#34;LPT7&#34;</span> <span class="s">&#34;LPT8&#34;</span> <span class="s">&#34;LPT9&#34;</span> </span></span><span class="line"><span class="cl"> } <span class="nb">member? </span>[ <span class="nb">drop </span><span class="s">&#34;file&#34;</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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ <span class="s">&#34;file&#34;</span> ] <span class="nb">when-empty </span><span class="k">; </span></span></span></code></pre></div><p>6. Filenames that begin with only a &ldquo;dot&rdquo; 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&#39;</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">&#34;file&#34;</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&#39;</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 -0700 https://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">&gt;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">&gt;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 &ldquo;pair rockets&rdquo; or &ldquo;hash rockets&rdquo;), 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">&gt;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">&gt;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 -0700 https://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&rsquo;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">&#34;,&#34;</span> split [ string&gt;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"> &#39;[ <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">&#34;~/trainingsample.csv&#34;</span> slurp-file </span></span><span class="line"><span class="cl"> <span class="s">&#34;~/validationsample.csv&#34;</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">&#34;Percentage correct: %.1f\n&#34;</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">&#34;,&#34;</span> split [ string&gt;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"> &#39;[ <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">&#34;~/trainingsample.csv&#34;</span> slurp-file </span></span><span class="line"><span class="cl"> <span class="s">&#34;~/validationsample.csv&#34;</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">&#34;Percentage correct: %f\n&#34;</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&rsquo;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 -0700 https://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> &lt;&lt; <span class="s">&#34;\x32&#34;</span> create-word-in 5/2 define-constant &gt;&gt; </span></span></code></pre></div><p>Then&hellip; 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 &ldquo;feature&rdquo;.</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&rsquo;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 -0700 https://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 &ldquo;paginator&rdquo;, 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>&lt;&lt; 1 2 ... 21 22 [23] 24 25 ... 27 28 &gt; </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&gt;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&gt;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 &ldquo;spec&rdquo; 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&gt;string ] </span></span><span class="line"><span class="cl"> [ page <span class="nb">= </span>[ <span class="s">&#34;[&#34;</span> <span class="s">&#34;]&#34;</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">&#34;...&#34;</span> ] <span class="nb">if* </span></span></span><span class="line"><span class="cl"> ] <span class="nb">map </span><span class="s">&#34; &#34;</span> <span class="nb">join </span><span class="s">&#34;&lt;&lt; &#34;</span> <span class="s">&#34; &gt;&gt;&#34;</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">&lt;&lt; [1] <span class="m">2 3 </span>... <span class="m">99 100 </span><span class="nb">&gt; </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">&lt;&lt; <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">&gt; </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">&lt;&lt; [1] <span class="m">2 3 </span><span class="nb">&gt; </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 -0700 https://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&rsquo;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&rsquo;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&rsquo;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/ &gt; </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 -0700 https://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 -0700 https://re.factorcode.org/2014/04/scraping-re-factor.html <p>For today&rsquo;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">&#34;/&#34;</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">&#34;feeds/posts/default?alt=json&amp;max-results=200&#34;</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&rsquo;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&gt; { <span class="s">&#34;feed&#34;</span> <span class="s">&#34;entry&#34;</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">&#34;title&#34;</span> <span class="nb">of </span><span class="s">&#34;$t&#34;</span> <span class="nb">of </span>] [ <span class="s">&#34;link&#34;</span> <span class="nb">of </span>] <span class="nb">bi </span></span></span><span class="line"><span class="cl"> <span class="nb">over </span>&#39;[ <span class="s">&#34;title&#34;</span> <span class="nb">of </span>_ <span class="nb">= </span>] <span class="nb">find nip </span><span class="s">&#34;href&#34;</span> <span class="nb">of </span></span></span><span class="line"><span class="cl"> &gt;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">&#34;title&#34;</span> <span class="s">&#34;$t&#34;</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">&lt;string&gt; 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">&#34;content&#34;</span> <span class="s">&#34;$t&#34;</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 -0700 https://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&gt;&gt; [ [ main&gt;&gt; ] <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">&#34;https://www.speedtest.net/speedtest-servers.php&#34;</span> </span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>string&gt;xml </span></span><span class="line"><span class="cl"> <span class="s">&#34;server&#34;</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>:&gt; radius <span class="c">! km</span> </span></span><span class="line"><span class="cl"> lat2 lat1 <span class="nb">- </span>radians :&gt; dlat </span></span><span class="line"><span class="cl"> lon2 lon1 <span class="nb">- </span>radians :&gt; 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>:&gt; 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>:&gt; 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">&#34;lat&#34;</span> <span class="nb">of </span>] [ <span class="s">&#34;lon&#34;</span> <span class="nb">of </span>] <span class="nb">bi </span>[ string&gt;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"> &#39;[ lat/lon _ _ geo-distance <span class="s">&#34;distance&#34;</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>&#39;[ _ _ server-distance ] <span class="nb">map </span></span></span><span class="line"><span class="cl"> [ <span class="s">&#34;distance&#34;</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">&lt;config&gt;</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">&#34;https://www.speedtest.net/speedtest-config.php&#34;</span> </span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>string&gt;xml { </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;client&#34;</span> deep-tag-named attr-map ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;times&#34;</span> deep-tag-named attr-map ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;download&#34;</span> deep-tag-named attr-map ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;upload&#34;</span> deep-tag-named attr-map ] </span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span>&lt;config&gt; <span class="k">; </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&gt;&gt; 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">&#34;url&#34;</span> <span class="nb">of </span>&gt;url <span class="s">URL&#34; latency.txt&#34;</span> derive-url </span></span><span class="line"><span class="cl"> [ http-get <span class="nb">nip </span><span class="s">&#34;test=test\n&#34;</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">&#34;latency&#34;</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 &ldquo;best&rdquo; 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">&#34;latency&#34;</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">&lt;string&gt; </span><span class="s">&#34;content1=&#34;</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">&#34;url&#34;</span> <span class="nb">of </span>&gt;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">&#34;upload&#34;</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">&#34;url&#34;</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">&#34;random%sx%s.jpg&#34;</span> sprintf &gt;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 &lt;array&gt; </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">&#34;download&#34;</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">&#34;Selecting best server based on ping...&#34;</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">&#34;sponsor&#34;</span> <span class="nb">of </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;name&#34;</span> <span class="nb">of </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;distance&#34;</span> <span class="nb">of </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;latency&#34;</span> <span class="nb">of </span>] </span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span><span class="s">&#34;Hosted by %s (%s) [%0.2f km]: %s ms\n&#34;</span> printf </span></span><span class="line"><span class="cl"> <span class="s">&#34;Testing download speed&#34;</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">&#34;download&#34;</span> <span class="nb">of </span><span class="s">&#34;Download: %0.2f Mbit/s\n&#34;</span> printf </span></span><span class="line"><span class="cl"> <span class="s">&#34;Testing upload speed&#34;</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">&#34;upload&#34;</span> <span class="nb">of </span><span class="s">&#34;Upload: %0.2f Mbit/s\n&#34;</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">&#34;download&#34;</span> <span class="nb">of </span><span class="m">1,000 </span><span class="nb">* &gt;integer </span><span class="s">&#34;download&#34;</span> ,, ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;latency&#34;</span> <span class="nb">of &gt;integer </span><span class="s">&#34;ping&#34;</span> ,, ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;upload&#34;</span> <span class="nb">of </span><span class="m">1,000 </span><span class="nb">* &gt;integer </span><span class="s">&#34;upload&#34;</span> ,, ] </span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">&#34;&#34;</span> <span class="s">&#34;promo&#34;</span> ,, ] </span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">&#34;pingselect&#34;</span> <span class="s">&#34;startmode&#34;</span> ,, ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;id&#34;</span> <span class="nb">of </span><span class="s">&#34;recommendedserverid&#34;</span> ,, ] </span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">&#34;1&#34;</span> <span class="s">&#34;accuracy&#34;</span> ,, ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;id&#34;</span> <span class="nb">of </span><span class="s">&#34;serverid&#34;</span> ,, ] </span></span><span class="line"><span class="cl"> [ </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;latency&#34;</span> <span class="nb">of </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;upload&#34;</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">&#34;download&#34;</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">&#34;%d-%d-%d-297aae72&#34;</span> sprintf md5 checksum-bytes </span></span><span class="line"><span class="cl"> hex-string <span class="s">&#34;hash&#34;</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">&#34;https://www.speedtest.net/api/api.php&#34;</span> </span></span><span class="line"><span class="cl"> &lt;post-request&gt; [ </span></span><span class="line"><span class="cl"> [ </span></span><span class="line"><span class="cl"> <span class="s">&#34;https://c.speedtest.net/flash/speedtest.swf&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;referer&#34;</span> </span></span><span class="line"><span class="cl"> ] <span class="nb">dip </span>header&gt;&gt; <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&gt;assoc <span class="s">&#34;resultid&#34;</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">&#34;Share results: &#34;</span> <span class="nb">write </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;https://www.speedtest.net/result/%s.png&#34;</span> sprintf </span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>&gt;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 &ldquo;retina&rdquo; 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 -0800 https://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">&#34;74.125.226.4&#34;</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">&#34;74.125.226.4&#34;</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&gt;string ] <span class="nb">with map </span><span class="s">&#34;.&#34;</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">&#34;.&#34;</span> split [ string&gt;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">&#34;74.125.226.4&#34;</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">&#34;74.125.226.4&#34;</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&gt;number</a> and <a href="https://docs.factorcode.org/content/word-number__gt__string,math.parser.html">number&gt;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&gt;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>&lt;iota&gt; [ number&gt;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&gt;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&gt;string ] <span class="nb">with map </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;.&#34;</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">&#34;.&#34;</span> split { <span class="m">24 16 8 0 </span>} </span></span><span class="line"><span class="cl"> [ [ string&gt;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&gt;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>&lt;iota&gt; [ number&gt;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&gt;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>&lt;iota&gt; <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&gt;string2 ] <span class="m">4 </span>napply <span class="nb">4array </span><span class="s">&#34;.&#34;</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">&#34;.&#34;</span> split <span class="nb">first4 </span>[ string&gt;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 -0800 https://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>&quot;FACTOR&quot;</code> by shifting each character to the right by three letters, we would get <code>&quot;IDFWRU&quot;</code>. The <code>&quot;F&quot;</code> shifts to <code>&quot;I&quot;</code>, the <code>&quot;A&quot;</code> shifts to <code>&quot;D&quot;</code>, the <code>&quot;C&quot;</code> shifts to <code>&quot;F&quot;</code>, etc.</p> <p>Let&rsquo;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>&quot;A&quot;</code> as our &ldquo;zero&rdquo; point by subtracting, shifting modulo 26 character ascii alphabet, then re-adding the ascii value for <code>&quot;A&quot;</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&#39;</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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> &#39;[ <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">&#34;HELLO, WORLD!&#34;</span> <span class="m">3 </span>caesar-encrypt <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;KHOOR, ZRUOG!&#34;</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">&#34;KHOOR, ZRUOG!&#34;</span> <span class="m">3 </span>caesar-decrypt <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;HELLO, WORLD!&#34;</span> </span></span></code></pre></div> Monte Carlo https://re.factorcode.org/2013/12/monte-carlo.html Fri, 13 Dec 2013 10:46:00 -0800 https://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">&lt;= </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 -0800 https://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">&#34;Factor&#34;</span> string&gt;uu <span class="nb">print </span></span></span><span class="line"><span class="cl">begin </span></span><span class="line"><span class="cl">&amp;1F%C=&amp;]R </span></span><span class="line"><span class="cl">end </span></span></code></pre></div><p>&hellip;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">&#34;&#34;&#34; </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"> &amp;1F%C=&amp;]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"> &#34;&#34;&#34;</span> uu&gt;string <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;Factor&#34;</span> </span></span></code></pre></div><p>Right now, it operates on text directly and doesn&rsquo;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 -0800 https://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 &ldquo;human-readable representations of digests&rdquo;. 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">&#34;7528880a986c40e78c38115e640da2a1&#34;</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">&#34;three-georgia-xray-jig&#34;</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">&#34;high-mango-white-oregon-purple-charlie&#34;</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">&#34;28129036-75a7-4c87-984b-4b32231e0a0d&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;nineteen-bluebird-oxygen-edward&#34;</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">&#34;ack&#34;</span> <span class="s">&#34;alabama&#34;</span> <span class="s">&#34;alanine&#34;</span> <span class="s">&#34;alaska&#34;</span> <span class="s">&#34;alpha&#34;</span> <span class="s">&#34;angel&#34;</span> <span class="s">&#34;apart&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;april&#34;</span> <span class="s">&#34;arizona&#34;</span> <span class="s">&#34;arkansas&#34;</span> <span class="s">&#34;artist&#34;</span> <span class="s">&#34;asparagus&#34;</span> <span class="s">&#34;aspen&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;august&#34;</span> <span class="s">&#34;autumn&#34;</span> <span class="s">&#34;avocado&#34;</span> <span class="s">&#34;bacon&#34;</span> <span class="s">&#34;bakerloo&#34;</span> <span class="s">&#34;batman&#34;</span> <span class="s">&#34;beer&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;berlin&#34;</span> <span class="s">&#34;beryllium&#34;</span> <span class="s">&#34;black&#34;</span> <span class="s">&#34;blossom&#34;</span> <span class="s">&#34;blue&#34;</span> <span class="s">&#34;bluebird&#34;</span> <span class="s">&#34;bravo&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;bulldog&#34;</span> <span class="s">&#34;burger&#34;</span> <span class="s">&#34;butter&#34;</span> <span class="s">&#34;california&#34;</span> <span class="s">&#34;carbon&#34;</span> <span class="s">&#34;cardinal&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;carolina&#34;</span> <span class="s">&#34;carpet&#34;</span> <span class="s">&#34;cat&#34;</span> <span class="s">&#34;ceiling&#34;</span> <span class="s">&#34;charlie&#34;</span> <span class="s">&#34;chicken&#34;</span> <span class="s">&#34;coffee&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;cola&#34;</span> <span class="s">&#34;cold&#34;</span> <span class="s">&#34;colorado&#34;</span> <span class="s">&#34;comet&#34;</span> <span class="s">&#34;connecticut&#34;</span> <span class="s">&#34;crazy&#34;</span> <span class="s">&#34;cup&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;dakota&#34;</span> <span class="s">&#34;december&#34;</span> <span class="s">&#34;delaware&#34;</span> <span class="s">&#34;delta&#34;</span> <span class="s">&#34;diet&#34;</span> <span class="s">&#34;don&#34;</span> <span class="s">&#34;double&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;early&#34;</span> <span class="s">&#34;earth&#34;</span> <span class="s">&#34;east&#34;</span> <span class="s">&#34;echo&#34;</span> <span class="s">&#34;edward&#34;</span> <span class="s">&#34;eight&#34;</span> <span class="s">&#34;eighteen&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;eleven&#34;</span> <span class="s">&#34;emma&#34;</span> <span class="s">&#34;enemy&#34;</span> <span class="s">&#34;equal&#34;</span> <span class="s">&#34;failed&#34;</span> <span class="s">&#34;fanta&#34;</span> <span class="s">&#34;fifteen&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;fillet&#34;</span> <span class="s">&#34;finch&#34;</span> <span class="s">&#34;fish&#34;</span> <span class="s">&#34;five&#34;</span> <span class="s">&#34;fix&#34;</span> <span class="s">&#34;floor&#34;</span> <span class="s">&#34;florida&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;football&#34;</span> <span class="s">&#34;four&#34;</span> <span class="s">&#34;fourteen&#34;</span> <span class="s">&#34;foxtrot&#34;</span> <span class="s">&#34;freddie&#34;</span> <span class="s">&#34;friend&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;fruit&#34;</span> <span class="s">&#34;gee&#34;</span> <span class="s">&#34;georgia&#34;</span> <span class="s">&#34;glucose&#34;</span> <span class="s">&#34;golf&#34;</span> <span class="s">&#34;green&#34;</span> <span class="s">&#34;grey&#34;</span> <span class="s">&#34;hamper&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;happy&#34;</span> <span class="s">&#34;harry&#34;</span> <span class="s">&#34;hawaii&#34;</span> <span class="s">&#34;helium&#34;</span> <span class="s">&#34;high&#34;</span> <span class="s">&#34;hot&#34;</span> <span class="s">&#34;hotel&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;hydrogen&#34;</span> <span class="s">&#34;idaho&#34;</span> <span class="s">&#34;illinois&#34;</span> <span class="s">&#34;india&#34;</span> <span class="s">&#34;indigo&#34;</span> <span class="s">&#34;ink&#34;</span> <span class="s">&#34;iowa&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;island&#34;</span> <span class="s">&#34;item&#34;</span> <span class="s">&#34;jersey&#34;</span> <span class="s">&#34;jig&#34;</span> <span class="s">&#34;johnny&#34;</span> <span class="s">&#34;juliet&#34;</span> <span class="s">&#34;july&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;jupiter&#34;</span> <span class="s">&#34;kansas&#34;</span> <span class="s">&#34;kentucky&#34;</span> <span class="s">&#34;kilo&#34;</span> <span class="s">&#34;king&#34;</span> <span class="s">&#34;kitten&#34;</span> <span class="s">&#34;lactose&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;lake&#34;</span> <span class="s">&#34;lamp&#34;</span> <span class="s">&#34;lemon&#34;</span> <span class="s">&#34;leopard&#34;</span> <span class="s">&#34;lima&#34;</span> <span class="s">&#34;lion&#34;</span> <span class="s">&#34;lithium&#34;</span> <span class="s">&#34;london&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;louisiana&#34;</span> <span class="s">&#34;low&#34;</span> <span class="s">&#34;magazine&#34;</span> <span class="s">&#34;magnesium&#34;</span> <span class="s">&#34;maine&#34;</span> <span class="s">&#34;mango&#34;</span> <span class="s">&#34;march&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;mars&#34;</span> <span class="s">&#34;maryland&#34;</span> <span class="s">&#34;massachusetts&#34;</span> <span class="s">&#34;may&#34;</span> <span class="s">&#34;mexico&#34;</span> <span class="s">&#34;michigan&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;mike&#34;</span> <span class="s">&#34;minnesota&#34;</span> <span class="s">&#34;mirror&#34;</span> <span class="s">&#34;mississippi&#34;</span> <span class="s">&#34;missouri&#34;</span> <span class="s">&#34;mobile&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;mockingbird&#34;</span> <span class="s">&#34;monkey&#34;</span> <span class="s">&#34;montana&#34;</span> <span class="s">&#34;moon&#34;</span> <span class="s">&#34;mountain&#34;</span> <span class="s">&#34;muppet&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;music&#34;</span> <span class="s">&#34;nebraska&#34;</span> <span class="s">&#34;neptune&#34;</span> <span class="s">&#34;network&#34;</span> <span class="s">&#34;nevada&#34;</span> <span class="s">&#34;nine&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;nineteen&#34;</span> <span class="s">&#34;nitrogen&#34;</span> <span class="s">&#34;north&#34;</span> <span class="s">&#34;november&#34;</span> <span class="s">&#34;nuts&#34;</span> <span class="s">&#34;october&#34;</span> <span class="s">&#34;ohio&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;oklahoma&#34;</span> <span class="s">&#34;one&#34;</span> <span class="s">&#34;orange&#34;</span> <span class="s">&#34;oranges&#34;</span> <span class="s">&#34;oregon&#34;</span> <span class="s">&#34;oscar&#34;</span> <span class="s">&#34;oven&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;oxygen&#34;</span> <span class="s">&#34;papa&#34;</span> <span class="s">&#34;paris&#34;</span> <span class="s">&#34;pasta&#34;</span> <span class="s">&#34;pennsylvania&#34;</span> <span class="s">&#34;pip&#34;</span> <span class="s">&#34;pizza&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;pluto&#34;</span> <span class="s">&#34;potato&#34;</span> <span class="s">&#34;princess&#34;</span> <span class="s">&#34;purple&#34;</span> <span class="s">&#34;quebec&#34;</span> <span class="s">&#34;queen&#34;</span> <span class="s">&#34;quiet&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;red&#34;</span> <span class="s">&#34;river&#34;</span> <span class="s">&#34;robert&#34;</span> <span class="s">&#34;robin&#34;</span> <span class="s">&#34;romeo&#34;</span> <span class="s">&#34;rugby&#34;</span> <span class="s">&#34;sad&#34;</span> <span class="s">&#34;salami&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;saturn&#34;</span> <span class="s">&#34;september&#34;</span> <span class="s">&#34;seven&#34;</span> <span class="s">&#34;seventeen&#34;</span> <span class="s">&#34;shade&#34;</span> <span class="s">&#34;sierra&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;single&#34;</span> <span class="s">&#34;sink&#34;</span> <span class="s">&#34;six&#34;</span> <span class="s">&#34;sixteen&#34;</span> <span class="s">&#34;skylark&#34;</span> <span class="s">&#34;snake&#34;</span> <span class="s">&#34;social&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;sodium&#34;</span> <span class="s">&#34;solar&#34;</span> <span class="s">&#34;south&#34;</span> <span class="s">&#34;spaghetti&#34;</span> <span class="s">&#34;speaker&#34;</span> <span class="s">&#34;spring&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;stairway&#34;</span> <span class="s">&#34;steak&#34;</span> <span class="s">&#34;stream&#34;</span> <span class="s">&#34;summer&#34;</span> <span class="s">&#34;sweet&#34;</span> <span class="s">&#34;table&#34;</span> <span class="s">&#34;tango&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;ten&#34;</span> <span class="s">&#34;tennessee&#34;</span> <span class="s">&#34;tennis&#34;</span> <span class="s">&#34;texas&#34;</span> <span class="s">&#34;thirteen&#34;</span> <span class="s">&#34;three&#34;</span> <span class="s">&#34;timing&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;triple&#34;</span> <span class="s">&#34;twelve&#34;</span> <span class="s">&#34;twenty&#34;</span> <span class="s">&#34;two&#34;</span> <span class="s">&#34;uncle&#34;</span> <span class="s">&#34;undress&#34;</span> <span class="s">&#34;uniform&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;uranus&#34;</span> <span class="s">&#34;utah&#34;</span> <span class="s">&#34;vegan&#34;</span> <span class="s">&#34;venus&#34;</span> <span class="s">&#34;vermont&#34;</span> <span class="s">&#34;victor&#34;</span> <span class="s">&#34;video&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;violet&#34;</span> <span class="s">&#34;virginia&#34;</span> <span class="s">&#34;washington&#34;</span> <span class="s">&#34;west&#34;</span> <span class="s">&#34;whiskey&#34;</span> <span class="s">&#34;white&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;william&#34;</span> <span class="s">&#34;winner&#34;</span> <span class="s">&#34;winter&#34;</span> <span class="s">&#34;wisconsin&#34;</span> <span class="s">&#34;wolfram&#34;</span> <span class="s">&#34;wyoming&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;xray&#34;</span> <span class="s">&#34;yankee&#34;</span> <span class="s">&#34;yellow&#34;</span> <span class="s">&#34;zebra&#34;</span> <span class="s">&#34;zulu&#34;</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">&lt; </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&rsquo;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>&lt;groups&gt; [ hex&gt; ] <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">&#34;-&#34;</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&rsquo;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 -0800 https://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 &ldquo;tzfile&rdquo;) 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 &ldquo;zoneinfo database&rdquo;). Each tzfile starts with the four magic bytes &ldquo;<code>TZif</code>&rdquo;, 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">&#34;TZif&#34;</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">&lt;tzfile&gt;</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&gt;&gt; [ read-be32 ] <span class="nb">replicate </span>] </span></span><span class="line"><span class="cl"> [ tzh_timecnt&gt;&gt; [ <span class="nb">read1 </span>] <span class="nb">replicate </span>] </span></span><span class="line"><span class="cl"> [ tzh_typecnt&gt;&gt; [ ttinfo read-struct ] <span class="nb">replicate </span>] </span></span><span class="line"><span class="cl"> [ tzh_charcnt&gt;&gt; <span class="nb">read </span>] </span></span><span class="line"><span class="cl"> [ tzh_leapcnt&gt;&gt; [ read-be32 read-be32 <span class="nb">2array </span>] <span class="nb">replicate </span>] </span></span><span class="line"><span class="cl"> [ tzh_ttisstdcnt&gt;&gt; <span class="nb">read </span>] </span></span><span class="line"><span class="cl"> [ tzh_ttisgmtcnt&gt;&gt; <span class="nb">read </span>] </span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span>&lt;tzfile&gt; <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">&lt;local-time&gt;</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">&lt;transition&gt;</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 &gt;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>&gt;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&gt;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&gt;&gt; tznames :&gt; abbrevs </span></span><span class="line"><span class="cl"> tzfile is-std&gt;&gt; :&gt; is-std </span></span><span class="line"><span class="cl"> tzfile is-gmt&gt;&gt; :&gt; is-gmt </span></span><span class="line"><span class="cl"> tzfile types&gt;&gt; [ </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&gt;&gt; seconds ] </span></span><span class="line"><span class="cl"> [ tt_isdst&gt;&gt; <span class="m">1 </span><span class="nb">= </span>] </span></span><span class="line"><span class="cl"> [ tt_abbrind&gt;&gt; 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>&lt;local-time&gt; </span></span><span class="line"><span class="cl"> ] <span class="nb">map-index </span>:&gt; local-times </span></span><span class="line"><span class="cl"> tzfile transition-times&gt;&gt; </span></span><span class="line"><span class="cl"> tzfile local-times&gt;&gt; [ </span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>unix-time&gt;timestamp ] [ local-times <span class="nb">nth </span>] <span class="nb">bi* </span></span></span><span class="line"><span class="cl"> &lt;transition&gt; </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">&lt;tzinfo&gt;</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&gt;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&gt;transitions &lt;tzinfo&gt; </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">&#34;/usr/share/zoneinfo/&#34;</span> <span class="nb">prepend </span>file&gt;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&gt;unix-time ] [ transitions&gt;&gt; ] <span class="nb">bi* </span></span></span><span class="line"><span class="cl"> [ [ seconds&gt;&gt; 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span>instant &gt;&gt;gmt-offset ] </span></span><span class="line"><span class="cl"> [ find-transition local-time&gt;&gt; gmt-offset&gt;&gt; ] <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&#39;</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 &ldquo;US/Pacific&rdquo; 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 &lt;timestamp&gt; </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">&#34;%c&#34;</span> strftime <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;Sun Oct 27 01:00:00 2002&#34;</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">&#34;US/Pacific&#34;</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">&#34;%c&#34;</span> strftime <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;Sun Oct 27 01:50:00 2002&#34;</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 -0800 https://re.factorcode.org/2013/11/n-numbers.html <p>In the United States, &ldquo;N-Numbers&rdquo; 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 &ldquo;I&rdquo; or &ldquo;O&rdquo; 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">&#34;IiOo&#34;</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">&#34;N&#34;</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&amp;&amp; ] </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&amp;&amp; ] </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&amp;&amp; ] </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">&#34;N&#34;</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&amp;&amp; <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 -0800 https://re.factorcode.org/2013/11/tree.html <p>I&rsquo;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">&#34;| &#34;</span> <span class="nb">write </span>] <span class="nb">times </span>] <span class="nb">unless-zero </span><span class="s">&#34;+-- &#34;</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&gt;&gt; <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&gt;&gt; ] <span class="nb">bi* </span>write-tree ] </span></span><span class="line"><span class="cl"> [ <span class="nb">3drop </span><span class="s">&#34; [error opening dir]&#34;</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&gt;&gt; ] 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&gt;&gt; +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">&#34;\n%d directories, %d files\n&#34;</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 -0800 https://re.factorcode.org/2013/11/messagepack.html <p><a href="https://msgpack.org">MessagePack</a> is an &ldquo;efficient binary serialization format&rdquo; 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 &ldquo;extended&rdquo; 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 &gt;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&gt; [ <span class="m">1 </span><span class="nb">read </span>signed-be&gt; ] <span class="nb">dip read 2array </span><span class="k">; </span></span></span></code></pre></div><p>We need a way to specify a &ldquo;nil&rdquo; (or &ldquo;null&rdquo;) 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&gt; ] } </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&gt; ] } </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&gt; ] } </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&gt; ] } </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&gt; ] } </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&gt; ] } </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&gt; ] } </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&gt; ] } </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&gt; <span class="nb">bits&gt;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&gt; <span class="nb">bits&gt;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">&#34;&#34;</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&gt; <span class="nb">read </span><span class="s">&#34;&#34;</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&gt; <span class="nb">read </span><span class="s">&#34;&#34;</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&gt; <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&gt; <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&gt; 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&gt; 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&gt; 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&gt; 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&gt; 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&gt; 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&rsquo;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 &ldquo;nil&rdquo; (or &ldquo;null&rdquo; 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">&gt;= </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">&lt;= </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">&lt;= </span>] [ <span class="m">0xcc </span><span class="nb">write1 </span><span class="m">1 </span>&gt;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">&lt;= </span>] [ <span class="m">0xcd </span><span class="nb">write1 </span><span class="m">2 </span>&gt;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">&lt;= </span>] [ <span class="m">0xce </span><span class="nb">write1 </span><span class="m">4 </span>&gt;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">&lt;= </span>] </span></span><span class="line"><span class="cl"> [ <span class="m">0xcf </span><span class="nb">write1 </span><span class="m">8 </span>&gt;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">&gt;= </span>] [ <span class="m">1 </span>&gt;be <span class="nb">write </span>] } </span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>-0x80 <span class="nb">&gt;= </span>] [ <span class="m">0xd0 </span><span class="nb">write1 </span><span class="m">1 </span>&gt;be <span class="nb">write </span>] } </span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>-0x8000 <span class="nb">&gt;= </span>] [ <span class="m">0xd1 </span><span class="nb">write1 </span><span class="m">2 </span>&gt;be <span class="nb">write </span>] } </span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>-0x80000000 <span class="nb">&gt;= </span>] [ <span class="m">0xd2 </span><span class="nb">write1 </span><span class="m">4 </span>&gt;be <span class="nb">write </span>] } </span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span>-0x8000000000000000 <span class="nb">&gt;= </span>] </span></span><span class="line"><span class="cl"> [ <span class="m">0xd3 </span><span class="nb">write1 </span><span class="m">8 </span>&gt;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&gt;bits </span><span class="m">8 </span>&gt;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">&lt;= </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">&lt;= </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">&lt;= </span>] [ <span class="m">0xda </span><span class="nb">write1 </span><span class="m">2 </span>&gt;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">&lt;= </span>] [ <span class="m">0xdb </span><span class="nb">write1 </span><span class="m">4 </span>&gt;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">&lt;= </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">&lt;= </span>] [ <span class="m">0xc5 </span><span class="nb">write1 </span><span class="m">2 </span>&gt;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">&lt;= </span>] [ <span class="m">0xc6 </span><span class="nb">write1 </span><span class="m">4 </span>&gt;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">&lt;= </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">&lt;= </span>] [ <span class="m">0xdc </span><span class="nb">write1 </span><span class="m">2 </span>&gt;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">&lt;= </span>] [ <span class="m">0xdd </span><span class="nb">write1 </span><span class="m">4 </span>&gt;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">&lt;= </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">&lt;= </span>] [ <span class="m">0xde </span><span class="nb">write1 </span><span class="m">2 </span>&gt;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">&lt;= </span>] [ <span class="m">0xdf </span><span class="nb">write1 </span><span class="m">4 </span>&gt;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&gt;</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">&gt;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 -0800 https://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 &lsquo;chucknorris&rsquo; is a color</a> on <a href="https://stackoverflow.com">StackOverflow</a>. This is called &ldquo;Flex Hex&rdquo; 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&rsquo;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&#39;</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&#39;</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 &ldquo;flex hex&rdquo; 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">&#34;#&#34;</span> ?head <span class="nb">drop </span>hex-only three-groups hex-rgb <span class="s">&#34;&#34;</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">&#34;00b000&#34;</span> } [ <span class="s">&#34;#zqbttv&#34;</span> flex-hex ] unit-test </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">{ <span class="s">&#34;0f0000&#34;</span> } [ <span class="s">&#34;f&#34;</span> flex-hex ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;000f00&#34;</span> } [ <span class="s">&#34;0f&#34;</span> flex-hex ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;000f00&#34;</span> } [ <span class="s">&#34;0f0&#34;</span> flex-hex ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;0f0f00&#34;</span> } [ <span class="s">&#34;0f0f&#34;</span> flex-hex ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;0ff000&#34;</span> } [ <span class="s">&#34;0f0f0f0&#34;</span> flex-hex ] unit-test </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">{ <span class="s">&#34;ad0e0e&#34;</span> } [ <span class="s">&#34;adamlevine&#34;</span> flex-hex ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;000000&#34;</span> } [ <span class="s">&#34;MrT&#34;</span> flex-hex ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;00c000&#34;</span> } [ <span class="s">&#34;sick&#34;</span> flex-hex ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;c0a000&#34;</span> } [ <span class="s">&#34;crap&#34;</span> flex-hex ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;c00000&#34;</span> } [ <span class="s">&#34;chucknorris&#34;</span> flex-hex ] unit-test </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">{ <span class="s">&#34;6ecde0&#34;</span> } [ </span></span><span class="line"><span class="cl"> <span class="s">&#34;6db6ec49efd278cd0bc92d1e5e072d68&#34;</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 -0800 https://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&rsquo;s latest <a href="https://hg.python.org/cpython/file/6fdbb81b4020/Modules/mathmodule.c#l1218">mathmodule.c</a>, and noticed an implementation of a &ldquo;divide-and-conquer factorial algorithm&rdquo;. 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">&gt; </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">&gt; </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&rsquo;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">+ &lt;= </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>:&gt; 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&#39;</span> <span class="nv">r&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> n <span class="m">2 </span><span class="nb">&gt; </span>[ </span></span><span class="line"><span class="cl"> n <span class="m">2 </span><span class="nb">/i </span>:&gt; 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 -0700 https://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>&rsquo;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&rsquo;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">&#34; &#34;</span> <span class="nb">join </span></span></span><span class="line"><span class="cl"> R/ (?&lt;=[.!?]|[.!?][\&#39;&#34;])\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">&#34;\n\n&#34;</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>&#39;[ </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">&lt; </span>[ <span class="nb">2drop </span><span class="s">&#34;&#34;</span> ] [ </span></span><span class="line"><span class="cl"> &#39;[ _ <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"> &#39;[ _ 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">&#34;&#34;</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 &ldquo;simple&rdquo; 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">&#34;simple&#34;</span> [ </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Programming&#34;</span> article. ] with-string-writer </span></span><span class="line"><span class="cl"> R/ \[\d+\]/ <span class="s">&#34;&#34;</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">&#34;machine form&#34;</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 -0700 https://re.factorcode.org/2013/10/text-to-speech.html <p>To those of us from a certain decade of computing, the phrase &ldquo;text-to-speech&rdquo; 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 &ldquo;<a href="https://www.x-entertainment.com/articles/0952/">Dr. Sbaitso was my only friend</a>&rdquo;.</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&rsquo;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">&#34;say \&#34;%s\&#34;&#34;</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">&#34;festival --tts&#34;</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 &ldquo;speak&rdquo; 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 -0700 https://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 &ldquo;compensated summation&rdquo; that &ldquo;<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>&rdquo;.</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&rsquo;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 &ldquo;simple sum&rdquo; 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">&gt;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">&gt;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 &ldquo;<code>48</code>&rdquo; instead of &ldquo;<code>82</code>&rdquo;):</p> <div class="highlight"><pre tabindex="0" class="chroma"><code 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 &ldquo;<code>86</code>&rdquo;):</p> <div class="highlight"><pre tabindex="0" class="chroma"><code 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&#39;</span> <span class="nv">sum&#39;</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 -0700 https://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 &ldquo;intransigence&rdquo; (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 &ldquo;palindrome&rdquo; 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 &ldquo;intransigence&rdquo; really is the longest palindrome!</p> <h3 id="palindrome">palindrome?</h3> <p>The basic definition for a &ldquo;palindrome&rdquo; is a word that &ldquo;reads the same backward as forward&rdquo;. Given that, it&rsquo;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 &ldquo;<em>A man, a plan, a canal: Panama.</em>&rdquo;:</p> <div class="highlight"><pre 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&#39;</span> <span class="nf">) </span>[ Letter? ] <span class="nb">filter </span>&gt;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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> normalize &gt;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">&#34;/usr/share/dict/words&#34;</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">&#34;incalescence&#34;</span> </span></span></code></pre></div><p>Wait, that isn&rsquo;t &ldquo;intransigence&rdquo;!</p> <p>Well, no, but &ldquo;incalescence&rdquo; has the same number of letters (13) and happens to be slightly longer in morse code. So maybe it&rsquo;s a tie, or maybe they should update their trivia!</p> <p>In fact, there are several &ldquo;longest&rdquo; 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">&#34;/usr/share/dict/words&#34;</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">&#34;incalescence&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;intercentral&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;predestinate&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;predestitute&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;protectorate&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Raphaelesque&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;ultranatural&#34;</span> </span></span><span class="line"><span class="cl">} </span></span></code></pre></div><p><em>P.S., it looks like &ldquo;Raphaelesque&rdquo; 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&rsquo;t contain the word &ldquo;intransigence&rdquo;.</em></p> AppleScript https://re.factorcode.org/2013/10/applescript.html Thu, 10 Oct 2013 16:24:00 -0700 https://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 &ldquo;plain English&rdquo;. 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 &ldquo;AppleScript Editor&rdquo; application.</p> <p>Wouldn&rsquo;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 -&gt; alloc ] <span class="nb">dip </span></span></span><span class="line"><span class="cl"> &lt;NSString&gt; -&gt; initWithSource: -&gt; autorelease </span></span><span class="line"><span class="cl"> <span class="no">f </span>-&gt; 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&rsquo;re curious to see what else you can do with AppleScript, check out <a href="https://dougscripts.com/itunes/">Doug&rsquo;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 -0700 https://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 &ldquo;rock-paper-scissors&rdquo; 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&rsquo;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> =&gt; <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">&#34;WIN&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="nb">2dup = </span>] [ <span class="s">&#34;TIE&#34;</span> ] } </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;LOSE&#34;</span> ] </span></span><span class="line"><span class="cl"> } <span class="nb">cond </span><span class="s">&#34;%s vs. %s: %s\n&#34;</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 -0700 https://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"> [ &gt;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"> &gt;hsva </span></span><span class="line"><span class="cl"> &gt;yiqa </span></span><span class="line"><span class="cl"> &gt;cmyka </span></span><span class="line"><span class="cl"> &gt;yuva </span></span><span class="line"><span class="cl"> &gt;hsla </span></span><span class="line"><span class="cl"> &gt;ryba </span></span><span class="line"><span class="cl"> &gt;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 -0700 https://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, &ldquo;YouTube downloader&rdquo; plugins have become a popular addition to most web browsers.</p> <p>I thought it would be fun to implement a basic &ldquo;YouTube downloader&rdquo; 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&#34; https://www.youtube.com/get_video_info&#34;</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">&#34;asv&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> <span class="s">&#34;detailpage&#34;</span> <span class="s">&#34;el&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> <span class="s">&#34;en_US&#34;</span> <span class="s">&#34;hl&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> <span class="nb">swap </span><span class="s">&#34;video_id&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>query&gt;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">&#34;url_encoded_fmt_stream_map&#34;</span> <span class="nb">of </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;,&#34;</span> split [ query&gt;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">&#34;url&#34;</span> <span class="nb">of </span>] [ <span class="s">&#34;sig&#34;</span> <span class="nb">of </span>] <span class="nb">bi </span><span class="s">&#34;&amp;signature=&#34;</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&#39;</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">&#34;\&#34;#$%&#39;*,./:;&lt;&gt;?^|~\\&#34;</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">&#34;type&#34;</span> <span class="nb">of </span><span class="s">&#34;video/mp4&#34;</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">&#34;title&#34;</span> <span class="nb">of </span>sanitize <span class="s">&#34;.mp4&#34;</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">&#34;~&#34;</span> [ <span class="s">&#34;G8LC8ES6ogw&#34;</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 -0700 https://re.factorcode.org/2013/09/zeromq.html <p><a href="https://zeromq.org">ZeroMQ</a> is a &ldquo;high-level&rdquo; socket library that provides some middleware capabilities and markets itself as <em>&ldquo;The Simplest Way to Connect Pieces&rdquo;</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 &ldquo;echo&rdquo; example.</p> <h3 id="echo">echo</h3> <p>Our &ldquo;echo&rdquo; 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"> &lt;zmq-context&gt; &amp;dispose </span></span><span class="line"><span class="cl"> ZMQ_REP &lt;zmq-socket&gt; &amp;dispose </span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">&#34;tcp://127.0.0.1:5000&#34;</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 &ldquo;echo&rdquo; 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"> &lt;zmq-context&gt; &amp;dispose </span></span><span class="line"><span class="cl"> ZMQ_REQ &lt;zmq-socket&gt; &amp;dispose </span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">&#34;tcp://127.0.0.1:5000&#34;</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">&#34;Sending &#34;</span> <span class="nb">write print flush </span>] </span></span><span class="line"><span class="cl"> [ &gt;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">&gt;string </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;Received &#34;</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 -0700 https://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 &ldquo;verbal expressions&rdquo;.</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">&#34;http&#34;</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">&#34;s&#34;</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">&#34;://&#34;</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">&#34;www.&#34;</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">&#34; &#34;</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&rsquo;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&rsquo;t as familiar with the esoteric syntax that is frequently required when matching text.</p> <p>These &ldquo;verbal&rdquo; 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&rsquo;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">&lt;verbexp&gt;</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">&#34;&#34;</span> <span class="s">&#34;&#34;</span> <span class="s">&#34;&#34;</span> <span class="s">&#34;&#34;</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">&gt;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&gt;&gt; ] [ source&gt;&gt; ] [ suffix&gt;&gt; ] <span class="nb">tri 3append </span>] </span></span><span class="line"><span class="cl"> [ modifiers&gt;&gt; ] <span class="nb">bi </span>&lt;optioned-regexp&gt; <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"> &#39;[ &lt;verbexp&gt; @ &gt;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"> &#39;[ _ <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&#39;</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">&#34;&#34;</span> make <span class="k">; </span></span></span></code></pre></div><h3 id="methods">Methods</h3> <p>We can specify &ldquo;anything&rdquo; or &ldquo;anything but&rdquo;:</p> <div class="highlight"><pre 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">&#34;(?:.*)&#34;</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">&#34;(?:[^&#34;</span> <span class="s">&#34;]*)&#34;</span> <span class="nb">surround </span>add <span class="k">; </span></span></span></code></pre></div><p>We can specify &ldquo;something&rdquo; and &ldquo;something but&rdquo;:</p> <div class="highlight"><pre 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">&#34;(?:.+)&#34;</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">&#34;(?:[^&#34;</span> <span class="s">&#34;]+)&#34;</span> <span class="nb">surround </span>add <span class="k">; </span></span></span></code></pre></div><p>We can specify looking for &ldquo;start of line&rdquo; or &ldquo;end of line&rdquo;:</p> <div class="highlight"><pre 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">&#34;^&#34;</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">&#34;$&#34;</span> <span class="nb">append </span>] change-suffix <span class="k">; </span></span></span></code></pre></div><p>We can specify a value (&ldquo;then&rdquo;), or an optional value (&ldquo;maybe&rdquo;):</p> <div class="highlight"><pre 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">&#34;(?:&#34;</span> <span class="s">&#34;)&#34;</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">&#34;(?:&#34;</span> <span class="s">&#34;)?&#34;</span> <span class="nb">surround </span>add <span class="k">; </span></span></span></code></pre></div><p>We could specify &ldquo;any of&rdquo; 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">&#34;(?:[&#34;</span> <span class="s">&#34;])&#34;</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">&#34;(?:(?:\\n)|(?:\\r\\n))&#34;</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">&#34;\\t&#34;</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">&#34;\\w+&#34;</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">&#34;\\s&#34;</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">&#34;*+&#34;</span> <span class="nb">member? </span>[ <span class="s">&#34;+&#34;</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"> &#39;[ _ <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"> &#39;[ _ <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">&#34;https://www.google.com&#34;</span> [ </span></span><span class="line"><span class="cl"> start-of-line </span></span><span class="line"><span class="cl"> <span class="s">&#34;http&#34;</span> then </span></span><span class="line"><span class="cl"> <span class="s">&#34;s&#34;</span> maybe </span></span><span class="line"><span class="cl"> <span class="s">&#34;://&#34;</span> then </span></span><span class="line"><span class="cl"> <span class="s">&#34;www.&#34;</span> maybe </span></span><span class="line"><span class="cl"> <span class="s">&#34; &#34;</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&rsquo;m not convinced this is an improvement. In the current specification for &ldquo;verbal&rdquo; 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 -0700 https://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&#34; https://domai.nr/api/json/search&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">swap </span><span class="s">&#34;q&#34;</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&gt; <span class="s">&#34;results&#34;</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&gt;&gt; ] [ path&gt;&gt; ] [ availability&gt;&gt; ] <span class="nb">tri </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;%s%s - %s\n&#34;</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 &ldquo;<em>factorcode</em>&rdquo; 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">&#34;factorcode&#34;</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&rsquo;m not sure that&rsquo;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 -0700 https://re.factorcode.org/2013/08/uniq.html <p>I&rsquo;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 &ldquo;uniq&rdquo; 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 &ldquo;main method&rdquo; 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 -0700 https://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 &ldquo;readable&rdquo; 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">&#34;KJFK&#34;</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">&#34;https://weather.noaa.gov/&#34;</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">&#34;pub/data/observations/metar/stations/%s.TXT&#34;</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 &ldquo;<code>Z</code>&rdquo; 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ now [ year&gt;&gt; ] [ month&gt;&gt; ] <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&gt;number ] <span class="nb">tri@ </span></span></span><span class="line"><span class="cl"> <span class="m">0 </span>instant &lt;timestamp&gt; timestamp&gt;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&hellip;</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 &ldquo;<code>V</code>&rdquo; 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 &ldquo;human&rdquo; 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">&#34;N&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">45.0 </span><span class="s">&#34;NE&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">90.0 </span><span class="s">&#34;E&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">135.0 </span><span class="s">&#34;SE&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">180.0 </span><span class="s">&#34;S&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">225.0 </span><span class="s">&#34;SW&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">270.0 </span><span class="s">&#34;W&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">315.0 </span><span class="s">&#34;NW&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">360.0 </span><span class="s">&#34;N&#34;</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&gt;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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">&#34;VRB&#34;</span> <span class="nb">= </span>[ <span class="nb">drop </span><span class="s">&#34;variable&#34;</span> ] [ </span></span><span class="line"><span class="cl"> string&gt;number [ direction&gt;compass ] <span class="nb">keep </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;from %s (%s°)&#34;</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&rsquo;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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">&#34;00000KT&#34;</span> <span class="nb">= </span>[ <span class="nb">drop </span><span class="s">&#34;calm&#34;</span> ] [ </span></span><span class="line"><span class="cl"> <span class="m">3 </span><span class="nb">cut </span><span class="s">&#34;KT&#34;</span> ?tail <span class="nb">drop </span><span class="s">&#34;G&#34;</span> split1 </span></span><span class="line"><span class="cl"> [ parse-direction ] [ string&gt;number ] [ string&gt;number ] <span class="nb">tri* </span></span></span><span class="line"><span class="cl"> [ <span class="s">&#34;%s at %s knots with gusts to %s knots&#34;</span> sprintf ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;%s at %s knots&#34;</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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;V&#34;</span> split1 [ string&gt;number [ direction&gt;compass ] <span class="nb">keep </span>] <span class="nb">bi@ </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;, variable from %s (%s°) to %s (%s°)&#34;</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 &ldquo;M1/4SM&rdquo; which means &ldquo;less than 1/4 statute miles&rdquo;, so we want to handle that. We also want to parse &ldquo;11/4&rdquo; as &ldquo;1+1/4&rdquo;:</p> <div class="highlight"><pre 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;M&#34;</span> ?head <span class="s">&#34;less than &#34;</span> <span class="s">&#34;&#34;</span> <span class="nb">? swap </span><span class="s">&#34;SM&#34;</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">&gt; </span>[ <span class="m">1 </span><span class="nb">cut </span><span class="s">&#34;+&#34;</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&gt;number <span class="s">&#34;%s%s statute miles&#34;</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">&#34;BC&#34;</span> <span class="s">&#34;patches&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;BL&#34;</span> <span class="s">&#34;blowing&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;BR&#34;</span> <span class="s">&#34;mist&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;DR&#34;</span> <span class="s">&#34;low drifting&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;DS&#34;</span> <span class="s">&#34;duststorm&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;DU&#34;</span> <span class="s">&#34;widespread dust&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;DZ&#34;</span> <span class="s">&#34;drizzle&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;FC&#34;</span> <span class="s">&#34;funnel clouds&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;FG&#34;</span> <span class="s">&#34;fog&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;FU&#34;</span> <span class="s">&#34;smoke&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;FZ&#34;</span> <span class="s">&#34;freezing&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;GR&#34;</span> <span class="s">&#34;hail&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;GS&#34;</span> <span class="s">&#34;small hail and/or snow pellets&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;HZ&#34;</span> <span class="s">&#34;haze&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;IC&#34;</span> <span class="s">&#34;ice crystals&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;MI&#34;</span> <span class="s">&#34;shallow&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;PL&#34;</span> <span class="s">&#34;ice pellets&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;PO&#34;</span> <span class="s">&#34;well-developed dust/sand whirls&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;PR&#34;</span> <span class="s">&#34;partial&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;PY&#34;</span> <span class="s">&#34;spray&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;RA&#34;</span> <span class="s">&#34;rain&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;RE&#34;</span> <span class="s">&#34;recent&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;SA&#34;</span> <span class="s">&#34;sand&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;SG&#34;</span> <span class="s">&#34;snow grains&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;SH&#34;</span> <span class="s">&#34;showers&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;SN&#34;</span> <span class="s">&#34;snow&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;SQ&#34;</span> <span class="s">&#34;squalls&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;SS&#34;</span> <span class="s">&#34;sandstorm&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;TS&#34;</span> <span class="s">&#34;thuderstorm&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;UP&#34;</span> <span class="s">&#34;unknown&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;VA&#34;</span> <span class="s">&#34;volcanic ash&#34;</span> } </span></span><span class="line"><span class="cl">} </span></span></code></pre></div><p>Weather severity defaults to moderate, but a leading &ldquo;<code>+</code>&rdquo; is used to indicate heavy weather and a &ldquo;<code>-</code>&rdquo; for light weather. Also, &ldquo;<code>+FC</code>&rdquo; 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">&#34;+FC&#34;</span> <span class="nb">= </span>[ <span class="nb">drop </span><span class="s">&#34;tornadoes or waterspouts&#34;</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">&#34;heavy &#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: - </span>[ <span class="nb">rest </span><span class="s">&#34;light &#34;</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">&#34; &#34;</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 &ldquo;<code>VC</code>&rdquo; 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;VC&#34;</span> <span class="nb">over subseq? </span>[ <span class="s">&#34;VC&#34;</span> <span class="s">&#34;&#34;</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">&#34; in the vicinity&#34;</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">&#34;BKN&#34;</span> <span class="s">&#34;broken&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;FEW&#34;</span> <span class="s">&#34;few&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;OVC&#34;</span> <span class="s">&#34;overcast&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;SCT&#34;</span> <span class="s">&#34;scattered&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;SKC&#34;</span> <span class="s">&#34;clear sky&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;CLR&#34;</span> <span class="s">&#34;clear sky&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;NSC&#34;</span> <span class="s">&#34;clear sky&#34;</span> } </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> { <span class="s">&#34;ACC&#34;</span> <span class="s">&#34;altocumulus castellanus&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;ACSL&#34;</span> <span class="s">&#34;standing lenticular altocumulus&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;CCSL&#34;</span> <span class="s">&#34;cirrocumulus standing lenticular cloud&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;CU&#34;</span> <span class="s">&#34;cumulus&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;SC&#34;</span> <span class="s">&#34;stratocumulus&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;SCSL&#34;</span> <span class="s">&#34;stratocumulus standing lenticular cloud&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;TCU&#34;</span> <span class="s">&#34;towering cumulus&#34;</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 &ldquo;<code>CAVOK</code>&rdquo;, which is sometimes used to mean &ldquo;<strong>C</strong>eiling <strong>a</strong>nd <strong>V</strong>isibility are <strong>OK</strong>&rdquo;:</p> <div class="highlight"><pre 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">&#34;CAVOK&#34;</span> <span class="nb">= </span>[ </span></span><span class="line"><span class="cl"> <span class="nb">drop </span><span class="s">&#34;clear skies and unlimited visibility&#34;</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&gt;number <span class="s">&#34; at %s00 ft&#34;</span> sprintf ] </span></span><span class="line"><span class="cl"> [ sky <span class="nb">at </span>[ <span class="s">&#34; (%s)&#34;</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 &ldquo;<code>M</code>&rdquo; 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">&#34;/&#34;</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">&#34;M&#34;</span> ?head [ string&gt;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">&#34;%s °C&#34;</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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="nb">unclip </span>[ string&gt;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">&#34;%.2f Hg&#34;</span> sprintf ] [ <span class="s">&#34;%s hPa&#34;</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&rsquo;s just save them raw and deal with parsing them later&hellip; 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>&#39;[ </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">&lt;report&gt;</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">&#34;RMK&#34;</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&lt;&lt; ] <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&lt;&lt; ] <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&lt;&lt; ] <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&gt;&gt; <span class="nb">prepend pick </span>wind&lt;&lt; ] <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&lt;&lt; ] <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">&#34;, &#34;</span> <span class="nb">join pick </span>weather&lt;&lt; </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">&#34;, &#34;</span> <span class="nb">join pick </span>sky-condition&lt;&lt; </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&lt;&lt; ] </span></span><span class="line"><span class="cl"> [ <span class="nb">pick </span>dew-point&lt;&lt; ] <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&lt;&lt; ] <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">&#34; &#34;</span> <span class="nb">join </span>&gt;&gt;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"> &#39;[ </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">&#34;Station&#34;</span> [ station&gt;&gt; ] row. ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Timestamp&#34;</span> [ timestamp&gt;&gt; ] row. ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Wind&#34;</span> [ wind&gt;&gt; ] row. ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Visibility&#34;</span> [ visibility&gt;&gt; ] row. ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Weather&#34;</span> [ weather&gt;&gt; ] row. ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Sky condition&#34;</span> [ sky-condition&gt;&gt; ] row. ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Temperature&#34;</span> [ temperature&gt;&gt; ] row. ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Dew point&#34;</span> [ dew-point&gt;&gt; ] row. ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Altimeter&#34;</span> [ altimeter&gt;&gt; ] row. ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Remarks&#34;</span> [ remarks&gt;&gt; ] 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 &lt;report&gt; report. ] </span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">&#34;%s METAR not found\n&#34;</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 -0700 https://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&hellip;</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">&lt;stdio.h&gt;</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">&lt;</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">&#34;x=%f</span><span class="se">\n</span><span class="s">&#34;</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>&hellip;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>&hellip;</p> <div class="highlight"><pre 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>&hellip;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 -0700 https://re.factorcode.org/2013/07/reading-list.html <p>The Safari browser has a feature called &ldquo;Reading List&rdquo; 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 &ldquo;Reading List&rdquo; 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">&#34;~/Library/Safari/Bookmarks.plist&#34;</span> read-plist </span></span><span class="line"><span class="cl"> <span class="s">&#34;Children&#34;</span> <span class="nb">of </span>[ <span class="s">&#34;Title&#34;</span> <span class="nb">of </span><span class="s">&#34;com.apple.ReadingList&#34;</span> <span class="nb">= </span>] <span class="nb">find nip </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;Children&#34;</span> <span class="nb">of </span>[ <span class="s">&#34;URLString&#34;</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">&#34;https://factorcode.org&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;https://news.ycombinator.com&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;https://reddit.com&#34;</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 -0700 https://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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> HS{ } <span class="nb">clone </span>:&gt; visited </span></span><span class="line"><span class="cl"> <span class="m">0 </span>seq <span class="nb">new-resizable </span>:&gt; 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">&#34;abcddd&#34;</span> non-repeating <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;abc&#34;</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">&#34;abcddd&#34;</span> non-repeating <span class="nb">first </span><span class="s">&#34;%c&#34;</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 -0700 https://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(&#34;/usr/share/terminfo\0&#34;, 0x7FFF5F21A120, 0x7FB279403AD0) access(&#34;/usr/share/terminfo/78/xterm-256color\0&#34;, 0x4, 0xE) open(&#34;/usr/share/terminfo/78/xterm-256color\0&#34;, 0x0, 0x0) read(0x3, &#34;\032\001%\0&#34;, 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">&lt;terminfo-header&gt;</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">&#34;ssssss&#34;</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">&#34;bad magic&#34;</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 &lt;terminfo-header&gt; <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 &ldquo;|&rdquo; character and terminated by a NUL byte (&ldquo;0&rdquo;):</p> <div class="highlight"><pre 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&gt;&gt; <span class="nb">read but-last </span><span class="s">&#34;|&#34;</span> split [ <span class="nb">&gt;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&gt;&gt; <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 &ldquo;odd&rdquo; 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&#39;</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>&lt;groups&gt; [ signed-le&gt; <span class="nb">dup </span><span class="m">0 </span><span class="nb">&lt; </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&gt;&gt; ] [ boolean-bytes&gt;&gt; ] <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&gt;&gt; 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&gt;&gt; read-shorts ] [ string-bytes&gt;&gt; <span class="nb">read </span>] <span class="nb">bi </span>&#39;[ </span></span><span class="line"><span class="cl"> [ _ <span class="m">0 </span><span class="nb">2over index-from swap subseq &gt;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 &ldquo;parse&rdquo; 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">&lt;terminfo&gt;</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>&lt;terminfo&gt; <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&gt;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">&#34;/usr/share/terminfo&#34;</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>&gt;hex ] <span class="nb">keep </span><span class="s">&#34;/usr/share/terminfo/%s/%s&#34;</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">&#34;/usr/share/terminfo/%c/%s&#34;</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 &ldquo;max_colors&rdquo; 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&gt;terminfo numbers&gt;&gt; <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">&#34;xterm-256color&#34;</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">&#34;xterm&#34;</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">&#34;vt100&#34;</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 -0700 https://re.factorcode.org/2013/04/factor-0-96-now-available.html <p><em>&ldquo;You&rsquo;re smart too late and old too soon.&rdquo; - Mike Tyson</em></p> <p>I&rsquo;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>&lt;groups&gt;</code>, <code>&lt;clumps&gt;</code>, and <code>&lt;circular-clumps&gt;</code> to use slices.</li> <li>Removed <code>&lt;slicing-groups&gt;</code>, <code>&lt;slicing-clumps&gt;</code>, <code>&lt;slicing-circular-clumps&gt;</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&rsquo;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">&ldquo;ping-compatible&rdquo; 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 &ldquo;libc&rdquo; 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">&ldquo;which&rdquo; command</a> (John Benediktsson)</li> </ul> Factorial https://re.factorcode.org/2013/04/factorial.html Mon, 15 Apr 2013 10:43:00 -0700 https://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">&gt; </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&rsquo;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 &ldquo;double factorial&rdquo;), 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 &gt; 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>&ldquo;descending factorial&rdquo;, &ldquo;falling sequential product&rdquo;, or &ldquo;lower factorial&rdquo;</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>&ldquo;ascending factorial&rdquo;, &ldquo;rising sequential product&rdquo;, or &ldquo;upper factorial&rdquo;</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&rsquo;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 -0700 https://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 &ldquo;bit hacks&rdquo; 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">&amp;</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">&amp;</span> <span class="o">-</span><span class="n">v</span><span class="p">))</span> <span class="o">&gt;&gt;</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>&#39;[ <span class="nb">dup </span>_ <span class="nb">&gt;= </span>] ] [ on-bits ] <span class="nb">bi* </span>] <span class="nb">dip swap </span></span></span><span class="line"><span class="cl"> &#39;[ _ [ 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 -0700 https://re.factorcode.org/2013/04/move.html <p>I&rsquo;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> (&ldquo;move&rdquo;) 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">&#34;Usage: move source ... target&#34;</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 &ldquo;main&rdquo; 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">&gt; </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&rsquo;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 -0700 https://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&gt;&gt; <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&gt;&gt; <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&gt;&gt; <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&gt;&gt; <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&gt;&gt; <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&gt;&gt; <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 -0800 https://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 &ldquo;shuffle&rdquo;</a> said:</p> <blockquote> <p><em>&ldquo;Turtle races are fun indeed.&rdquo;</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&rsquo;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 &ldquo;shuffle&rdquo; 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>&lt;iota&gt; <span class="nb">&gt;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 &ldquo;shuffle&rdquo;)</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 &ldquo;turtle&rdquo; into a &ldquo;hare&rdquo;. Every little bit helps!</p> Fast "shuffle" https://re.factorcode.org/2013/02/fast-shuffle.html Tue, 26 Feb 2013 15:28:00 -0800 https://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&rsquo;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 &ldquo;<code>10,000,000 &lt;iota&gt; &gt;array</code>&rdquo;). 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">&gt; </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">&gt; </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">&gt; </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">&gt; </span>] </span></span><span class="line"><span class="cl"> random-generator <span class="nb">get </span>&#39;[ </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">&gt; </span>] </span></span><span class="line"><span class="cl"> random-generator <span class="nb">get </span>&#39;[ </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&rsquo;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">&gt; </span>] </span></span><span class="line"><span class="cl"> random-generator <span class="nb">get </span>&#39;[ </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">&lt;</span><span class="n">array</span><span class="o">&gt;</span><span class="p">(</span><span class="n">ctx</span><span class="o">-&gt;</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">&gt;</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">&gt;</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">&#39;took </span><span class="si">%.3f</span><span class="s1"> seconds&#39;</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">&#39;took </span><span class="si">%.3f</span><span class="s1"> seconds&#39;</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 &ldquo;pure&rdquo; 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">&#34;took %.3f seconds&#34;</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">&#34;took %.3f seconds&#34;</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&rsquo;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 -0800 https://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 &ldquo;readers can understand the meaning of words in a sentence even when the interior letters of each word are scrambled&rdquo;.</p> <p>Can you read this?</p> <blockquote> <p><em>&hellip;it deosn&rsquo;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&rsquo;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 &ldquo;misspell&rdquo; 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="nb">dup </span>[ <span class="s">&#34;,&#39;.:;!?&#34;</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">&gt; </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 &ldquo;misspell&rdquo; 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&#39;</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">&#34; &#34;</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&#39;</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">&#34;\n&#34;</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">&#34;this really works!&#34;</span> misspell <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;this relaly wroks!&#34;</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 -0800 https://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 &ldquo;<em>concise, easier to read, and easier to maintain</em>&rdquo;. I&rsquo;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&rsquo;s task in Factor is quite simple.</p> <p>The problem statement is to &ldquo;<em>convert the keys of two maps into a comma-separated string</em>&rdquo; 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 &ldquo;, &quot; as a separator</li> <li>Return &ldquo;&lt;none&gt;&rdquo; 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">&#34;&lt;none&gt;&#34;</span> ] [ </span></span><span class="line"><span class="cl"> [ present ] <span class="nb">map </span>natural-sort <span class="s">&#34;, &#34;</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">&#34;&lt;none&gt;&#34;</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">&#34;barney, fred, wilma&#34;</span> } [ </span></span><span class="line"><span class="cl"> H{ { <span class="s">&#34;fred&#34;</span> <span class="no">t </span>} } </span></span><span class="line"><span class="cl"> H{ { <span class="s">&#34;barney&#34;</span> <span class="no">t </span>} { <span class="s">&#34;wilma&#34;</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">&#34;fred&#34;</span> } [ H{ { <span class="s">&#34;fred&#34;</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">&#34;barney, fred&#34;</span> } [ </span></span><span class="line"><span class="cl"> H{ { <span class="s">&#34;fred&#34;</span> <span class="no">t </span>} } </span></span><span class="line"><span class="cl"> H{ { <span class="s">&#34;fred&#34;</span> <span class="no">t </span>} { <span class="s">&#34;barney&#34;</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">&#34;&lt;none&gt;&#34;</span> ] [ </span></span><span class="line"><span class="cl"> [ present ] <span class="nb">map </span>natural-sort <span class="s">&#34;, &#34;</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 -0800 https://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 &ldquo;<em>the full path of the executables that would have been executed when this argument had been entered at the shell prompt</em>&rdquo;. 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&amp;&amp; <span class="k">; </span></span></span></code></pre></div><p>The Windows convention is to separate a list of paths with a &ldquo;<code>;</code>&rdquo; and on Mac and Linux to use a &ldquo;<code>:</code>&rdquo;. We&rsquo;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">&#34;;&#34;</span> <span class="s">&#34;:&#34;</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 &ldquo;path extensions&rdquo; 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">&#34;PATHEXT&#34;</span> os-env [ </span></span><span class="line"><span class="cl"> split-path <span class="nb">2dup </span>[ [ &gt;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&gt;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">&#34;.&#34;</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">&#34;PATH&#34;</span> os-env (which) <span class="k">; </span></span></span></code></pre></div><p>Here&rsquo;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">&#34;python&#34;</span> which <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;/usr/bin/python&#34;</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">&#34;ping&#34;</span> which <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;/sbin/ping&#34;</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">&#34;does-not-exist&#34;</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 -0800 https://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&rsquo;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&gt;&gt; ] <span class="nb">bi </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;https://en.wikipedia.org/wiki/%s_%s&#34;</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 &ldquo;Events&rdquo;. The third list contains &ldquo;Births&rdquo; and the fourth contains &ldquo;Deaths&rdquo;.</p> <div class="highlight"><pre 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&gt;xml </span></span><span class="line"><span class="cl"> <span class="s">&#34;ul&#34;</span> deep-tags-named <span class="nb">second </span>children-tags </span></span><span class="line"><span class="cl"> [ deep-children&gt;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 -0800 https://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&rsquo;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 -0800 https://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">&ldquo;resolution independence&rdquo;</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 -0700 https://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 &ldquo;3n+1&rdquo; 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&rsquo;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 &ldquo;cycle sequence&rdquo; 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">&gt; </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 &ldquo;cycle length&rdquo; 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">&gt; </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&rsquo;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&gt;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">&gt; </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 &ldquo;main&rdquo; 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&gt;number ] <span class="nb">bi@ 2dup </span>[a..b] max-cycle-length </span></span><span class="line"><span class="cl"> <span class="s">&#34;%s %s %s\n&#34;</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 &lt; 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 -0700 https://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 &ldquo;random replacement&rdquo; 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">&#34;&#34;&#34;Return an element chosen at random from `seq`.&#34;&#34;&#34;</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 &ldquo;line number&rdquo; (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>&#39;[ <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>:&gt; accum </span></span><span class="line"><span class="cl"> [| line line# | </span></span><span class="line"><span class="cl"> line# n <span class="nb">&lt;= </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 :&gt; r </span></span><span class="line"><span class="cl"> r n <span class="nb">&lt; </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 -0700 https://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:\&gt;ping 1 Pinging 0.0.0.1 with 32 bytes of data: C:\&gt;ping 1.2 Pinging 1.0.0.2 with 32 bytes of data: C:\&gt;ping 1.2.3 Pinging 1.2.0.3 with 32 bytes of data: C:\&gt;ping 1.2.3.4 Pinging 1.2.3.4 with 32 bytes of data: C:\&gt;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:\&gt;ping 255 Pinging 0.0.0.255 with 32 bytes of data: C:\&gt;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">&#34;.&#34;</span> split [ string&gt;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&gt;string ] <span class="nb">map </span><span class="s">&#34;.&#34;</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">&#34;0&#34;</span> <span class="nb">head? </span>] [ <span class="s">&#34;0x&#34;</span> <span class="nb">head? not </span>] } 1&amp;&amp; </span></span><span class="line"><span class="cl"> [ <span class="m">1 </span><span class="nb">tail </span><span class="s">&#34;0o&#34;</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">&#34;.&#34;</span> split [ cleanup-octal string&gt;number ] <span class="nb">map </span><span class="k">; </span></span></span></code></pre></div><p>And if we want to support the &ldquo;carry propagation&rdquo; which allows <code>256</code> to mean <code>0.0.1.0</code>, we need to &ldquo;bubble&rdquo; 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&#39;</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&gt;string ] <span class="nb">map </span><span class="s">&#34;.&#34;</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&hellip;</p> COLOR: <TAB> https://re.factorcode.org/2012/09/color-tab.html Mon, 24 Sep 2012 18:58:00 -0700 https://re.factorcode.org/2012/09/color-tab.html <p>As a diversion today, I added &ldquo;color tab completion&rdquo; 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 &ldquo;colors-matching&rdquo; word</a> and then <a href="https://github.com/factor/factor/commit/a8e0ccfd5f4ca32b27fe42a5a7693a28f0bb49ab">added a &ldquo;colors-completion&rdquo; type</a> to the UI.</p> Faster Tables! https://re.factorcode.org/2012/09/faster-tables.html Tue, 18 Sep 2012 17:15:00 -0700 https://re.factorcode.org/2012/09/faster-tables.html <p>We&rsquo;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&rsquo;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 &ldquo;virtually unscrollable&rdquo;.</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 &ldquo;baseline alignment&rdquo; 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 &ldquo;aligned gadget&rdquo; 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 -0700 https://re.factorcode.org/2012/09/watching-words.html <p>Function &ldquo;annotations&rdquo; 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> &ndash; 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&rsquo;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 &ldquo;watch&rdquo; 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 &ldquo;reset&rdquo; on it (or right-clicking on its definition in the UI and choosing &ldquo;reset&rdquo;) 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 &ldquo;watching&rdquo;, 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 &ldquo;before&rdquo; and &ldquo;after&rdquo; 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">&#34;hi&#34;</span> <span class="nb">print </span>] [ <span class="s">&#34;bye&#34;</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 -0700 https://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 &ldquo;comment out&rdquo; the descriptive text, they &ldquo;comment in&rdquo; 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>&gt;</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&rsquo;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>&gt;</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> &lt; <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">&lt;literate-lexer&gt;</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&gt;&gt; <span class="nb">zero? </span>[ </span></span><span class="line"><span class="cl"> <span class="nb">dup </span>line-text&gt;&gt; [ </span></span><span class="line"><span class="cl"> <span class="s">&#34;&gt; &#34;</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>&lt;LITERATE </span></span><span class="line"><span class="cl"> <span class="s">&#34;LITERATE&gt;&#34;</span> parse-multiline-string string-lines [ </span></span><span class="line"><span class="cl"> &lt;literate-lexer&gt; (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> &lt;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&#39;t... maybe the following works: </span></span><span class="line"><span class="cl"> <span class="nb">&gt; </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">&gt; </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">&gt; </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">&gt; </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 -0700 https://re.factorcode.org/2012/08/factor-0-95-now-available.html <p><em>&ldquo;The reports of my death are greatly exaggerated&rdquo; - Mark Twain</em></p> <p>I&rsquo;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 &ldquo;ping&rdquo; implementation</li> <li>DNS client and &ldquo;host&rdquo; 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 &ldquo;<code>&lt;int&gt;</code>&rdquo; to &ldquo;<code>int &lt;ref&gt;</code>&rdquo; and &ldquo;<code>*int</code>&rdquo; to &ldquo;<code>int deref</code>&rdquo;</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 &ldquo;<code>( -- )</code>&rdquo; and &ldquo;<code>(( -- ))</code>&rdquo; stack effect syntax</li> <li>Change prepend to return type of first sequence to match append behavior</li> <li>Change &ldquo;.factor-rc&rdquo; to be &ldquo;.factor-rc&rdquo; 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 &ldquo;99 bottles of beer&rdquo; (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 &ldquo;lint&rdquo; 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">&ldquo;tagged netstrings&rdquo;</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&rsquo;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 &ldquo;-run&rdquo; 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 &ldquo;~&rdquo; 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 &lt;raw&gt; 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&gt;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&rsquo;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 &ldquo;toggle fullscreen&rdquo; (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>&gt;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 -0700 https://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 &ldquo;echo server&rdquo; to demonstrate how the libraries work.</p> <p>The basic logic for an &ldquo;echo server&rdquo; 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 &ldquo;echo server&rdquo; 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>&quot;echo.server&quot;</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">&lt;echo-server&gt;</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 &lt;threaded-server&gt; </span></span><span class="line"><span class="cl"> <span class="nb">swap </span>&gt;&gt;insecure </span></span><span class="line"><span class="cl"> <span class="s">&#34;echo.server&#34;</span> &gt;&gt;name </span></span><span class="line"><span class="cl"> [ echo-loop ] &gt;&gt;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>&lt;echo-server&gt; 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 -0700 https://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">&#34;TERM&#34;</span> os-env <span class="s">&#34;-256color&#34;</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 &ldquo;256color&rdquo; 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 &ldquo;256color&rdquo; 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 &ldquo;256color&rdquo; with the smallest &ldquo;distance&rdquo; (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&gt;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&gt;&gt; ] [ green&gt;&gt; ] [ blue&gt;&gt; ] <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">&gt;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&gt;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&gt;rgb &#39;[ _ 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&gt;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&gt;256color <span class="s">&#34;\u00001b[38;5;%sm&#34;</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&gt;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&gt;256color <span class="s">&#34;\u00001b[48;5;%sm&#34;</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&rsquo;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 -0700 https://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 &ldquo;magic packet&rdquo; 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&rsquo;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">&#34;:-&#34;</span> split [ hex&gt; ] 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 &ldquo;magic packet&rdquo; 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* &lt;array&gt; 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 &ldquo;magic packet&rdquo;, 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>&lt;inet4&gt; ] <span class="nb">bi* </span></span></span><span class="line"><span class="cl"> <span class="no">f </span><span class="m">0 </span>&lt;inet4&gt; &lt;broadcast&gt; [ send ] with-disposal <span class="k">; </span></span></span></code></pre></div><p>It&rsquo;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 &ldquo;Wake for network access&rdquo; 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 -0700 https://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&rsquo;s worth pointing out that Factor has many &ldquo;builtin&rdquo; 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 &ldquo;obvious&rdquo; 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>&#39;[ <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 &ldquo;linear&rdquo; solution uses sets to track which elements we&rsquo;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 &#39;[ _ ?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>&lt;bit-set&gt; &#39;[ _ ?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 -0700 https://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 &ldquo;fake names&rdquo;. 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 &ldquo;seed&rdquo; 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 &ldquo;transitions&rdquo;. 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 &ldquo;transition&rdquo; 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">&lt;enum&gt; </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>&#39;[ <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">&#34;hello&#34;</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">&#34;&#34;</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">&#34;Sooyllan&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Brakalan&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Napl&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Drizi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Tevorri&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Flladian&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Skadi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Cynocak&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Pamu&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Patha&#34;</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 -0700 https://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),&hellip; 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&rsquo;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>&lt;clumps&gt; [ <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&amp;&amp; <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&hellip;</p> Faster Big Ratios! https://re.factorcode.org/2012/04/faster-big-ratios.html Thu, 05 Apr 2012 10:22:00 -0700 https://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&rsquo;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">&gt;= </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 &ldquo;bignums&rdquo;. 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&rsquo;s GCD algorithm</a> began degrading particularly due to the (relatively) expensive calculation of &ldquo;mod&rdquo; for bignum&rsquo;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&rsquo;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&rsquo;s GCD, I created a <a href="https://github.com/factor/factor/commit/1b8f1d9945adaaaa039bef3a6f07a8fbfe3cfb3b">fast-gcd</a> word that used this for bignum&rsquo;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 -0700 https://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 &ldquo;main&rdquo; numbers (1 through 56) and then pick a &ldquo;mega&rdquo; number (1 through 46).</p> <p>We can use the &ldquo;n choose k&rdquo; formula in <a href="https://docs.factorcode.org/content/vocab-math.combinatorics.html">math.combinatorics</a> to compute the number of ways of picking &ldquo;<em>5</em> unordered outcomes from <em>56</em> numbers and then <em>1</em> of <em>46</em> possible mega numbers&rdquo;:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code 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&rsquo;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">&#34;$%.2f&#34;</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&hellip; well, Factor to the rescue! Let&rsquo;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 -0800 https://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 &ldquo;next greater permutation of digits&rdquo; 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 &ldquo;lexicographically next greater permutation of elements&rdquo;. 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&rsquo;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">&lt;</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">&lt;</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">&lt;</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 &ldquo;cut point&rdquo; 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">&gt; </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 &ldquo;cut point&rdquo; (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">&gt; </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&rsquo;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">&#34;&#34;</span> } [ <span class="s">&#34;&#34;</span> next-permutation ] unit-test </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">{ <span class="s">&#34;1&#34;</span> } [ <span class="s">&#34;1&#34;</span> next-permutation ] unit-test </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">{ <span class="s">&#34;21&#34;</span> } [ <span class="s">&#34;12&#34;</span> next-permutation ] unit-test </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">{ <span class="s">&#34;8344112666&#34;</span> } [ <span class="s">&#34;8342666411&#34;</span> next-permutation ] unit-test </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">{ <span class="s">&#34;ABC&#34;</span> <span class="s">&#34;ACB&#34;</span> <span class="s">&#34;BAC&#34;</span> <span class="s">&#34;BCA&#34;</span> <span class="s">&#34;CAB&#34;</span> <span class="s">&#34;CBA&#34;</span> <span class="s">&#34;ABC&#34;</span> } </span></span><span class="line"><span class="cl">[ <span class="s">&#34;ABC&#34;</span> <span class="m">6 </span>[ <span class="nb">dup &gt;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&gt;string next-permutation string&gt;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 -0800 https://re.factorcode.org/2012/02/txon.html <p>The <a href="https://www.hxa.name/txon">TXON</a>, also known as &ldquo;Text Object Notation&rdquo;, 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 &ldquo;`&rdquo; 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="s">R&#34; `&#34;</span> <span class="s">&#34;\\`&#34;</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">&gt;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">&gt;txon</span> </span></span><span class="line"><span class="cl"> [ &gt;txon ] <span class="nb">map </span><span class="s">&#34;\n&#34;</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">&gt;txon</span> </span></span><span class="line"><span class="cl"> <span class="nb">&gt;alist </span>[ </span></span><span class="line"><span class="cl"> <span class="nb">first2 </span>[ encode-value ] [ &gt;txon ] <span class="nb">bi* </span><span class="s">&#34;%s:`%s`&#34;</span> sprintf </span></span><span class="line"><span class="cl"> ] <span class="nb">map </span><span class="s">&#34;\n&#34;</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">&gt;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">&gt;txon</span> </span></span><span class="line"><span class="cl"> number&gt;string &gt;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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="s">R&#34; \\`&#34;</span> <span class="s">&#34;`&#34;</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">&#34;:`&#34;</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 &ldquo;name=value&rdquo; 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 &ldquo;name=value&rdquo; 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">&#34;:`&#34;</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 &ldquo;name=value&rdquo; 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&amp;&amp; ] </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 &ldquo;name=value&rdquo; 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&gt;</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">&#34;a&#34;</span> <span class="s">&#34;123&#34;</span> } } &gt;txon <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;a:`123`&#34;</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">&#34;a:`123`&#34;</span> txon&gt; <span class="m">. </span></span></span><span class="line"><span class="cl">H{ { <span class="s">&#34;a&#34;</span> <span class="s">&#34;123&#34;</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 -0800 https://re.factorcode.org/2012/02/readability.html <p>James O&rsquo;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 &ldquo;readability&rdquo; 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">&lt;?</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">&gt;</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">&gt;</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">-&gt;</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&rsquo;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">&gt;</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">&gt;&gt;&gt;</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">&gt;</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">&gt;&gt;&gt;</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">&gt; </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">&gt; </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 -0800 https://re.factorcode.org/2012/02/copy.html <p>I&rsquo;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> (&ldquo;copy&rdquo;) 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">&#34;Usage: copy source ... target&#34;</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&amp;&amp; </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">&gt; </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 -0800 https://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 &ldquo;unique&rdquo; <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&gt;rgba</h3> <p>We calculate timestamps as an offset from <a href="https://en.wikipedia.org/wiki/Dennis_Ritchie">Dennis Ritchie</a>&rsquo;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>&lt;date&gt; <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&gt;seconds <span class="nb">&gt;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&gt;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 &lt;rgba&gt; </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&gt;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&gt;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">&lt;rgba-clock&gt;</h3> <p>Let&rsquo;s use the <code>timestamp&gt;rgba</code> word to make an updating &ldquo;colored clock&rdquo;. 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&gt;&gt; background&lt;&lt; ] </span></span><span class="line"><span class="cl"> [ [ &lt;solid&gt; ] <span class="nb">dip </span>[ interior&lt;&lt; ] [ boundary&lt;&lt; ] <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">&lt;rgba-clock&gt;</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>&lt;label-control&gt; </span></span><span class="line"><span class="cl"> time <span class="nb">get over </span>&#39;[ </span></span><span class="line"><span class="cl"> [ timestamp&gt;rgba _ update-colors ] </span></span><span class="line"><span class="cl"> [ timestamp&gt;hms ] <span class="nb">bi </span></span></span><span class="line"><span class="cl"> ] &lt;arrow&gt; &gt;&gt;model </span></span><span class="line"><span class="cl"> <span class="s">&#34;HH:MM:SS&#34;</span> &gt;&gt;string </span></span><span class="line"><span class="cl"> monospace-font &gt;&gt;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> &lt;rgba-clock&gt; 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 -0800 https://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 &ldquo;<em>occurs at least once, but at most three times a year</em>&rdquo;.</p> <h3 id="friday-13th">friday-13th?</h3> <p>A day is &ldquo;Friday the 13th&rdquo; 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&gt;&gt; <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&rsquo;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>&lt;date&gt; <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&gt;&gt; <span class="m">13 </span><span class="nb">&gt;= </span>[ <span class="m">1 </span>months time+ ] <span class="nb">when </span><span class="m">13 </span>&gt;&gt;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 -0800 https://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&#39;</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">&#34;--verbose&#34;</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"> &#39;[ <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">&gt; </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">&#34;%s:\n&#34;</span> printf ] [ </span></span><span class="line"><span class="cl"> [ <span class="nb">dup </span>md5-file <span class="s">&#34; %s\n %s\n&#34;</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">&#34;Total duped files found: %d\n&#34;</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 -0800 https://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&rsquo;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 -0800 https://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&rsquo;t be fooled by claims that the casino has the &ldquo;loosest slots&rdquo;, 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">&#34;☀☁☂☃&#34;</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&#39;</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">&lt; </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">&lt; </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 &ldquo;spin&rdquo; 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 (&ldquo;0,0&rdquo;) 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">&#34;\e[0;0H\e[2J&#34;</span> <span class="nb">write </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;Welcome to the Factor slot machine!&#34;</span> <span class="nb">print nl </span></span></span><span class="line"><span class="cl"> <span class="s">&#34; +--------+&#34;</span> <span class="nb">print </span></span></span><span class="line"><span class="cl"> <span class="s">&#34; | CASINO |&#34;</span> <span class="nb">print </span></span></span><span class="line"><span class="cl"> <span class="s">&#34; |--------| *&#34;</span> <span class="nb">print </span></span></span><span class="line"><span class="cl"> <span class="nb">3dup </span><span class="s">&#34; |%c |%c |%c | |\n&#34;</span> printf </span></span><span class="line"><span class="cl"> <span class="s">&#34; |--------|/&#34;</span> <span class="nb">print </span></span></span><span class="line"><span class="cl"> <span class="s">&#34; | [_] |&#34;</span> <span class="nb">print </span></span></span><span class="line"><span class="cl"> <span class="s">&#34; +--------+&#34;</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 &ldquo;pay line&rdquo; 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">&#34;You WIN!&#34;</span> <span class="s">&#34;You LOSE!&#34;</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>&lt;iota&gt; [ 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">&#34;Press ENTER to play again.&#34;</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 &ldquo;MAIN&rdquo; 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 -0800 https://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&rsquo;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&rsquo;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 &ldquo;make any words out of elements in the periodic table&rdquo;. 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">&#34;H&#34;</span> <span class="s">&#34;Hydrogen&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;He&#34;</span> <span class="s">&#34;Helium&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;Li&#34;</span> <span class="s">&#34;Lithium&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;Be&#34;</span> <span class="s">&#34;Beryllium&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;B&#34;</span> <span class="s">&#34;Boron&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;C&#34;</span> <span class="s">&#34;Carbon&#34;</span> } </span></span><span class="line"><span class="cl"> ... </span></span><span class="line"><span class="cl"> { <span class="s">&#34;Uut&#34;</span> <span class="s">&#34;Ununtrium&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;Uuq&#34;</span> <span class="s">&#34;Ununquadium&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;Uup&#34;</span> <span class="s">&#34;Ununpentium&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;Uuh&#34;</span> <span class="s">&#34;Ununhexium&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;Uus&#34;</span> <span class="s">&#34;Ununseptium&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;Uuo&#34;</span> <span class="s">&#34;Ununoctium&#34;</span> } </span></span><span class="line"><span class="cl"> } [ [ &gt;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 &gt; </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 &ldquo;periodic&rdquo; 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"> &#39;[ <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"> &gt;lower <span class="m">0 </span>(periodic?) <span class="k">; </span></span></span></code></pre></div><p>It&rsquo;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">&#34;/usr/share/dict/words&#34;</span> ascii file-lines <span class="k">; </span></span></span></code></pre></div><p>And then a list of all &ldquo;periodic words&rdquo;:</p> <div class="highlight"><pre 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 &ldquo;periodic words&rdquo;? 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 -0800 https://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&rsquo;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 &ldquo;gold standard&rdquo; 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 &ldquo;<code>USE: system [ image wc-file-lines ] time</code>&rdquo;):</p> <div class="highlight"><pre 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&rsquo;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">&#34;\n&#34;</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&rsquo;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 &lt;file-reader&gt; [ </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 &lt;file-reader&gt; [ </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 + &gt;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">&#34;wc -l &#34;</span> <span class="nb">prepend </span>utf8 [ </span></span><span class="line"><span class="cl"> <span class="nb">readln </span><span class="s">&#34; &#34;</span> split <span class="nb">harvest first </span>string&gt;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 &ldquo;faster&rdquo; 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 &lt;file-reader&gt; &amp;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">+ &gt;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 + &gt;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 -0700 https://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 &ldquo;optimized output&rdquo; 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">&#34;COMPLEX SHUFFLE&#34;</span> <span class="s">&#34;COMPLEX SHUFFLE&#34;</span> R&gt; <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">&#34;Hello, world&#34;</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">&#34;Hello, world&#34;</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">&#34;Hello, world&#34;</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 -0700 https://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 &lt;&lt; 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&gt;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 &gt;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 &lt;iota&gt; [ 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 &lt;iota&gt; [ 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 &lt;iota&gt; [ 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">&gt;float </span>2^int 2^frac <span class="nb">* &gt;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&gt;parts ] <span class="nb">keep </span><span class="m">0 </span><span class="nb">&gt;= </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">&lt; </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">* &gt;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&rsquo;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&rsquo;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">&lt; </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">/ &gt;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 -0700 https://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 + &hellip;) 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&rsquo;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&#39;</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">&gt;= </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">&lt;</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 -0700 https://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">&gt;&gt;</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">=&gt;</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">&gt;&gt;&gt;</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">&gt;</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">&gt;&gt;&gt;</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&rsquo;m not going to go over all the details, but if you&rsquo;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&gt;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&gt;bits bitor bits&gt;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">&gt; </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&gt;bits bitor bits&gt;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">&gt;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">&gt;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>&#39;[ </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">&gt;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&rsquo;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 -0700 https://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 (&ldquo;a&rdquo; to &ldquo;z&rdquo;):</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl"><span class="k">:</span> <span class="nf">&lt;alphabet&gt;</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>&lt;iota&gt; <span class="nb">&gt;array </span><span class="k">; </span></span></span></code></pre></div><p>The Enigma machine is made up of a number of &ldquo;cogs&rdquo;, 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">&lt;cog&gt;</span> <span class="nf">( -- </span><span class="nv">cog</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> &lt;alphabet&gt; 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&#39;</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 &ldquo;reflector&rdquo; 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">&lt;reflector&gt;</span> <span class="nf">( -- </span><span class="nv">reflector</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> &lt;alphabet&gt; <span class="nb">dup length </span>&lt;iota&gt; [ <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">&lt;enigma&gt;</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"> [ &lt;cog&gt; ] <span class="nb">replicate dup clone </span>&lt;reflector&gt; 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">&gt; </span>] [ <span class="m">0 </span><span class="nb">&lt; </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&#39;</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&rsquo;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>:&gt; ln! </span></span><span class="line"><span class="cl"> enigma cogs&gt;&gt; :&gt; cogs </span></span><span class="line"><span class="cl"> enigma reflector&gt;&gt; :&gt; reflector </span></span><span class="line"><span class="cl"> text &gt;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>&lt;iota&gt; [ <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&gt;&gt; &gt;&gt;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>&lt;enigma&gt; </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">&#34;hello, world&#34;</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 -0700 https://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 -0700 https://re.factorcode.org/2011/09/manipulating-files.html <p>Java has had historically frustrating API&rsquo;s for interacting with files. In Java 7, these API&rsquo;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&rsquo;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">&#34;/path/to/file&#34;</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">&#34;/path/to/file&#34;</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 &ldquo;rw-rw-rw-&rdquo; and &ldquo;g+x&rdquo; 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">&#34;/path/to/file&#34;</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">&#34;/path/from&#34;</span> <span class="s">&#34;/path/to&#34;</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">&#34;/path/from&#34;</span> <span class="s">&#34;/path/to&#34;</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">&#34;/dir1/file&#34;</span> <span class="s">&#34;/dir2&#34;</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">&#34;/path/from&#34;</span> <span class="s">&#34;/path/to&#34;</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">&#34;/dir1/file&#34;</span> <span class="s">&#34;/dir2&#34;</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 -0700 https://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 &ldquo;display a line of text&rdquo;. 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 &ldquo;<em>not print the trailing newline character</em>&rdquo;. 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ <span class="nb">first </span><span class="s">&#34;-n&#34;</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">&#34; &#34;</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 &ldquo;main&rdquo; 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 -0700 https://re.factorcode.org/2011/09/fun-with-wav.html <p>Last year, a &ldquo;<a href="https://yannesposito.com/Scratch/en/blog/2010-10-14-Fun-with-wav/">Fun with wav</a>&rdquo; 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 &ldquo;winner&rdquo; 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">&#39;Z4 i Z8 i s s i i s s Z4 i s*&#39;</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">&amp;</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">&lt;&lt;</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">&#34;</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">&#34;</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 &ldquo;golf&rdquo;, 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> =&gt; <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 &ldquo;master RIFF chunk&rdquo; 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&gt;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 &ldquo;wavsum&rdquo; 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">&#34;vocab:wavsum/truck.wav&#34;</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 &ldquo;extended WAV&rdquo; 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 -0700 https://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 &ldquo;zero load time&rdquo; 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 &ldquo;pointers&rdquo; 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&gt; <span class="k">; </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">&#34;\0&#34;</span> <span class="nb">read-until drop &gt;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 &ldquo;nth&rdquo; 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 &ldquo;nth&rdquo; 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 :&gt; high! <span class="m">-1 </span>:&gt; low! <span class="no">f </span>:&gt; candidate! </span></span><span class="line"><span class="cl"> [ high low <span class="nb">- </span><span class="m">1 </span><span class="nb">&gt; </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>:&gt; probe </span></span><span class="line"><span class="cl"> probe nth-word candidate! </span></span><span class="line"><span class="cl"> candidate word &lt;=&gt; { </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>:&gt; ptr </span></span><span class="line"><span class="cl"> ptr read-int :&gt; #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 &ldquo;related words&rdquo; 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">&lt;thesaurus-reader&gt;</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">&#34;vocab:thesaurus/thesaurus.dat&#34;</span> binary &lt;file-reader&gt; <span class="k">; </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"> [ &lt;thesaurus-reader&gt; ] <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">&#34;food&#34;</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">&#34;aliment&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;bread&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;chow&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;comestibles&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;commons&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;eatables&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;eats&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;edibles&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;feed&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;foodstuff&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;foodstuffs&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;grub&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;meat&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;nourishment&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;nurture&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;nutriment&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;pabulum&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;pap&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;provender&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;provisions&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;rations&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;scoff&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;subsistence&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;sustenance&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;tuck&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;viands&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;victuals&#34;</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 -0700 https://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> &ldquo;string utility&rdquo; 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 &ldquo;successor&rdquo; of a string is defined to be a kind of &ldquo;alphanum increment&rdquo;. It&rsquo;s easiest to show a few examples of how it works:</p> <pre tabindex="0"><code>successor(&#34;a&#34;) == &#34;b&#34; successor(&#34;1&#34;) == &#34;2&#34; successor(&#34;abcd&#34;) == &#34;abce&#34; successor(&#34;THX1138&#34;) == &#34;THX1139&#34; successor(&#34;&lt;&lt;koala&gt;&gt;&#34;) == &#34;&lt;&lt;koalb&gt;&gt;&#34; successor(&#34;1999zzz&#34;) == &#34;2000aaa&#34; successor(&#34;ZZZ9999&#34;) == &#34;AAAA0000&#34; </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>&ldquo;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.&rdquo;</p> </blockquote> <p>To start, we should handle the &ldquo;carry&rdquo; 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> &#39;[ _ <span class="nb">&gt; 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&#39;</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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ ch&gt;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&gt;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&#39;</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&#39;</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">&gt; 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&#39;</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 -0700 https://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 &ldquo;each unique line from <code>stdin</code>&rdquo;:</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 &ldquo;current input stream&rdquo;, 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>&#39;[ </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 -0700 https://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 &ldquo;string utility&rdquo; 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 &ldquo;humanized&rdquo; strings such as &ldquo;1st, 2nd, 3rd or 4th&rdquo;.</p> <blockquote> <p><em>Note: this function is sometimes called &ldquo;ordinalize&rdquo; - 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">&gt;=</span> <span class="mi">11</span> <span class="o">&amp;&amp;</span> <span class="nx">number</span> <span class="o">%</span> <span class="mi">100</span> <span class="o">&lt;=</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">&#34;th&#34;</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">&#34;st&#34;</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">&#34;nd&#34;</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">&#34;rd&#34;</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">&#34;th&#34;</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">&#34;th&#34;</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">&#34;st&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">2 </span>[ <span class="s">&#34;nd&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">3 </span>[ <span class="s">&#34;rd&#34;</span> ] } </span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">&#34;th&#34;</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&gt;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">&#34;1st&#34;</span> } [ <span class="m">1 </span>humanize ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;2nd&#34;</span> } [ <span class="m">2 </span>humanize ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;3rd&#34;</span> } [ <span class="m">3 </span>humanize ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;4th&#34;</span> } [ <span class="m">4 </span>humanize ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;11th&#34;</span> } [ <span class="m">11 </span>humanize ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;12th&#34;</span> } [ <span class="m">12 </span>humanize ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;13th&#34;</span> } [ <span class="m">13 </span>humanize ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;21st&#34;</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 -0700 https://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 &ldquo;formatted printing&rdquo;, 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">&#34;There are %d monkeys&#34;</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&rsquo;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">&#34;%&#34;</span> =&gt; [[ [ <span class="s">&#34;%&#34;</span> ] ]] </span></span><span class="line"><span class="cl">fmt-c <span class="nb">= </span><span class="s">&#34;c&#34;</span> =&gt; [[ [ <span class="nb">1string </span>] ]] </span></span><span class="line"><span class="cl">fmt-s <span class="nb">= </span><span class="s">&#34;s&#34;</span> =&gt; [[ [ present ] ]] </span></span><span class="line"><span class="cl">fmt-d <span class="nb">= </span><span class="s">&#34;d&#34;</span> =&gt; [[ [ <span class="nb">&gt;integer </span>number&gt;string ] ]] </span></span><span class="line"><span class="cl">fmt-f <span class="nb">= </span><span class="s">&#34;f&#34;</span> =&gt; [[ [ <span class="nb">&gt;float </span>number&gt;string ] ]] </span></span><span class="line"><span class="cl">fmt-x <span class="nb">= </span><span class="s">&#34;x&#34;</span> =&gt; [[ [ &gt;hex ] ]] </span></span><span class="line"><span class="cl">unknown <span class="nb">= </span>(.)* =&gt; [[ <span class="nb">&gt;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">&#34;%&#34;</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>(!(&#34;%&#34;).)+ </span></span><span class="line"><span class="cl"> =&gt; [[ <span class="nb">&gt;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"> =&gt; [[ [ <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">&#34;There are %d monkeys&#34;</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">&#34;There are &#34;</span> , ] </span></span><span class="line"><span class="cl"> [ <span class="nb">&gt;integer </span>number&gt;string , ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34; monkeys&#34;</span> , ] </span></span><span class="line"><span class="cl">} </span></span></code></pre></div><p>The &ldquo;printf&rdquo; <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">&#34;There are %d monkeys&#34;</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">&#34; monkeys&#34;</span> , <span class="nb">&gt;integer </span>number&gt;string , <span class="s">&#34;There are &#34;</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">&#34;&#34;</span> } [ <span class="s">&#34;&#34;</span> sprintf ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;asdf&#34;</span> } [ <span class="s">&#34;asdf&#34;</span> sprintf ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;10&#34;</span> } [ <span class="m">10 </span><span class="s">&#34;%d&#34;</span> sprintf ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;-10&#34;</span> } [ <span class="m">-10 </span><span class="s">&#34;%d&#34;</span> sprintf ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;ff&#34;</span> } [ <span class="m">0xff </span><span class="s">&#34;%x&#34;</span> sprintf ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;Hello, World!&#34;</span> } [ <span class="s">&#34;Hello, World!&#34;</span> <span class="s">&#34;%s&#34;</span> sprintf ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;printf test&#34;</span> } [ <span class="s">&#34;printf test&#34;</span> sprintf ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;char a = &#39;a&#39;&#34;</span> } [ <span class="sc">CHAR: a </span><span class="s">&#34;char %c = &#39;a&#39;&#34;</span> sprintf ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;0 message(s)&#34;</span> } [ <span class="m">0 </span><span class="s">&#34;message&#34;</span> <span class="s">&#34;%d %s(s)&#34;</span> sprintf ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;10%&#34;</span> } [ <span class="m">10 </span><span class="s">&#34;%d%%&#34;</span> sprintf ] unit-test </span></span><span class="line"><span class="cl">{ <span class="s">&#34;[monkey]&#34;</span> } [ <span class="s">&#34;monkey&#34;</span> <span class="s">&#34;[%s]&#34;</span> sprintf ] unit-test </span></span></code></pre></div><p>This implementation doesn&rsquo;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 -0700 https://re.factorcode.org/2011/08/fizzbuzz.html <p>The &ldquo;new classic&rdquo; 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&rsquo;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">&gt;&gt;&gt;</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">&#34;FizzBuzz&#34;</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">&#34;Fizz&#34;</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">&#34;Buzz&#34;</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">=&gt;</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">=&gt;</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">&#34;FizzBuzz&#34;</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">&#34;Fizz&#34;</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">&#34;Buzz&#34;</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">&#34;FizzBuzz&#34;</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">&#34;Fizz&#34;</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">&#34;Buzz&#34;</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&rsquo;s see if we can improve the <a href="https://factorcode.org/">Factor</a> version a bit. First, we can &ldquo;factor out&rdquo; 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">&#34;FizzBuzz&#34;</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">&#34;Fizz&#34;</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">&#34;Buzz&#34;</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 &ldquo;cond-case&rdquo; 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">&#34;FizzBuzz&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="m">3 </span> divisor? ] [ <span class="s">&#34;Fizz&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="m">5 </span> divisor? ] [ <span class="s">&#34;Buzz&#34;</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">&#34;Fizz&#34;</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">&#34;Buzz&#34;</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">&#34;&#34;</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 -0700 https://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 &ldquo;secret code&rdquo;.</p> <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">&#34;https://robohash.org/&#34;</span> <span class="nb">prepend </span>&gt;url <span class="k">; </span></span></span></code></pre></div><p>Next, we would like to support the different &ldquo;image sets&rdquo; 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">&#34;set&#34;</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: &ldquo;set1&rdquo;, &ldquo;set2&rdquo;, and &ldquo;set3&rdquo;):</p> <div class="highlight"><pre 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">&#34;set1&#34;</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">&#34;set2&#34;</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">&#34;set3&#34;</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 -0700 https://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 &ldquo;Moore’s Voting Algorithm&rdquo;.</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 -0700 https://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 &ldquo;one-liners&rdquo; in Clojure, I thought I&rsquo;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 &ldquo;doubling&rdquo; <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">&#34;factor&#34;</span> <span class="s">&#34;concatenative&#34;</span> <span class="s">&#34;stack-based&#34;</span> } </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;factor is awesome&#34;</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">&#34;/path/to/file.txt&#34;</span> utf8 file-contents </span></span></code></pre></div><p>Sing the four verses to the &ldquo;Happy Birthday&rdquo; 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">&#34;Happy Birthday &#34;</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">&#34;dear NAME&#34;</span> <span class="s">&#34;to You&#34;</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">&gt; </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">&#34;https://search.twitter.com/search.atom?q=factor&#34;</span> </span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>string&gt;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">&#34;https://apple.com&#34;</span> http-get <span class="nb">drop </span></span></span><span class="line"><span class="cl"> header&gt;&gt; <span class="s">&#34;server&#34;</span> <span class="nb">swap at </span><span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;Apache/2.2.3 (Oracle)&#34;</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">&#34;474e552773204e6f7420556e6978&#34;</span> </span></span><span class="line"><span class="cl"> <span class="m">2 </span>&lt;groups&gt; [ <span class="m">16 </span>base&gt; ] <span class="s">&#34;&#34;</span> <span class="nb">map-as </span><span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;GNU&#39;s Not Unix&#34;</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 -0700 https://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 &ldquo;long enough&rdquo; <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 &ldquo;suspect text&rdquo;, 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 &ldquo;n&rdquo; 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>] [ &lt;clumps&gt; ] <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&gt;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">&#34;[\\W\\S]&#34;</span> <span class="nb">join </span>] <span class="nb">map </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;\\s+&#34;</span> <span class="nb">join </span><span class="s">&#34;(\\s|^)&#34;</span> <span class="s">&#34;(\\s|$)&#34;</span> <span class="nb">surround </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;i&#34;</span> &lt;optioned-regexp&gt; <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&#39;</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 &ldquo;n-gram regexp&rdquo;, we can <a href="https://docs.factorcode.org/content/word-ch__gt__upper,unicode.data.html">ch&gt;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&gt;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&#39;</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&gt;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 &ldquo;main method&rdquo; 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">&lt; </span>[ </span></span><span class="line"><span class="cl"> <span class="nb">drop </span><span class="s">&#34;USAGE: plagiarism N suspect.txt source.txt...&#34;</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&gt;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 &ldquo;really&rdquo;):</p> <div class="highlight"><pre tabindex="0" class="chroma"><code 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;this is a really long piece of text&#34;</span> </span></span><span class="line"><span class="cl"> { <span class="s">&#34;this is a long piece of text&#34;</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&rsquo;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 &ldquo;n or more&rdquo;. 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 -0700 https://re.factorcode.org/2011/07/concatenative-thinking.html <p>I&rsquo;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/">&ldquo;Functional Thinking&rdquo;</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 &ldquo;best&rdquo; 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">&lt;</span><span class="n">Integer</span><span class="o">&gt;</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">&lt;</span><span class="n">Integer</span><span class="p">,</span><span class="w"> </span><span class="n">Boolean</span><span class="o">&gt;</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">&lt;</span><span class="n">Integer</span><span class="o">&gt;</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">&gt;</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">&lt;</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 &ldquo;imperative&rdquo; 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">&gt; </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">&lt; </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 -0700 https://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 &ldquo;substring&rdquo; functions. Since I couldn&rsquo;t find them in <a href="https://www.factorcode.org/">Factor&rsquo;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] [ &lt;clumps&gt; ] <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">&#34;abcd&#34;</span> all-subseqs <span class="m">. </span></span></span><span class="line"><span class="cl">{ <span class="s">&#34;a&#34;</span> <span class="s">&#34;b&#34;</span> <span class="s">&#34;c&#34;</span> <span class="s">&#34;d&#34;</span> <span class="s">&#34;ab&#34;</span> <span class="s">&#34;bc&#34;</span> <span class="s">&#34;cd&#34;</span> <span class="s">&#34;abc&#34;</span> <span class="s">&#34;bcd&#34;</span> <span class="s">&#34;abcd&#34;</span> } </span></span></code></pre></div><p><em>Note: we specifically don&rsquo;t include the &ldquo;empty&rdquo; 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>:&gt; len1 </span></span><span class="line"><span class="cl"> seq2 <span class="nb">length </span>:&gt; len2 </span></span><span class="line"><span class="cl"> <span class="m">0 </span>:&gt; n! </span></span><span class="line"><span class="cl"> <span class="m">0 </span>:&gt; 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">&lt;array&gt; </span>] <span class="nb">replicate </span>:&gt; 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>:&gt; 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">&gt; </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">&#34;abc&#34;</span> <span class="s">&#34;def&#34;</span> longest-subseq <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;&#34;</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">&#34;abcd&#34;</span> <span class="s">&#34;abcde&#34;</span> longest-subseq <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;abcd&#34;</span> </span></span></code></pre></div><p><em>Note: don&rsquo;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 -0700 https://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&rsquo;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">&#34;%d bottles of beer on the wall, &#34;</span> printf </span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">&#34;%d bottles of beer.\n&#34;</span> printf </span></span><span class="line"><span class="cl"> <span class="s">&#34;Take one down and pass it around, &#34;</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">&#34;%d bottles of beer on the wall.\n&#34;</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">&#34;1 bottle of beer on the wall, &#34;</span> <span class="nb">write </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;1 bottle of beer.&#34;</span> <span class="nb">print </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;Take one down and pass it around, &#34;</span> <span class="nb">write </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;no more bottles of beer on the wall.&#34;</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">&#34;No more bottles of beer on the wall, &#34;</span> <span class="nb">write </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;no more bottles of beer.&#34;</span> <span class="nb">print </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;Go to the store and buy some more, &#34;</span> <span class="nb">write </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;99 bottles of beer on the wall.&#34;</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">&#34;%d bottles of beer on the wall, &#34;</span> printf </span></span><span class="line"><span class="cl"> <span class="nb">dup </span><span class="s">&#34;%d bottles of beer.\n&#34;</span> printf </span></span><span class="line"><span class="cl"> <span class="s">&#34;Take one down and pass it around, &#34;</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">&#34;%d bottles of beer on the wall.\n&#34;</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">&#34;Go to the store and buy some more, &#34;</span> <span class="nb">write </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;no more bottles of beer on the wall!&#34;</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">&#34;1 bottle&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0 </span>[ <span class="s">&#34;no more bottles&#34;</span> ] } </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;%d bottles&#34;</span> sprintf ] </span></span><span class="line"><span class="cl"> } <span class="nb">case </span><span class="s">&#34; of beer&#34;</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">&#34;%s on the wall, %s.\n&#34;</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">&#34;Take one down and pass it around, &#34;</span> <span class="nb">write </span></span></span><span class="line"><span class="cl"> #bottles <span class="s">&#34;%s on the wall.\n&#34;</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">&#34;Go to the store and buy some more, &#34;</span> <span class="nb">write </span></span></span><span class="line"><span class="cl"> #bottles <span class="s">&#34;%s on the wall.\n&#34;</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&rsquo;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&rsquo;s a clear win. But, if we want to simply generate the &ldquo;bottles song&rdquo;, 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&rsquo;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">&#34;say \&#34;%s\&#34;&#34;</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 -0700 https://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 &ldquo;biggest beer-selling holiday of the year&rdquo;. 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&rsquo;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>&lt;date&gt; <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>&lt;date&gt; 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>&lt;date&gt; 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>&lt;date&gt; day-name <span class="s">&#34;%s: %s\n&#34;</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>&lt;date&gt; <span class="m">2010 9 18 </span>&lt;date&gt; </span></span><span class="line"><span class="cl"> time- duration&gt;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 -0700 https://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> (&ldquo;term frequency - inverse document frequency&rdquo;) 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 &ldquo;stop&rdquo;) words.</li> </ol> <p>Fist, let&rsquo;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 &ldquo;stop words&rdquo; that are to be ignored. These are typically common occurring words such as &ldquo;<em>and, in, or, of, the, is</em>&rdquo;.</p> <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">&#34;vocab:tf-idf/stopwords.txt&#34;</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 &ldquo;stop words&rdquo;.</p> <div class="highlight"><pre 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"> &gt;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&gt;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 &ldquo;database&rdquo;:</p> <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">&lt;db&gt;</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 &ldquo;inverse document frequency&rdquo; 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&gt;&gt; ] [ index&gt;&gt; <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 &ldquo;TF-IDF&rdquo; scores by multiplying the number of times a term appears in each document by the previously calculated &ldquo;inverse document frequency&rdquo;:</p> <div class="highlight"><pre 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&gt;&gt; <span class="nb">at </span>] [ idf ] <span class="nb">2bi </span>&#39;[ _ <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 &ldquo;TF-IDF&rdquo; 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>&#39;[ _ 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&gt;&gt; <span class="nb">at </span>] <span class="nb">keep </span>&#39;[ _ 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> &#39;[ <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 &ldquo;union&rdquo;, 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&rsquo;t exist produces a &ldquo;divide by zero&rdquo; error. Making it more robust requires some changes to either catch and ignore that error or to &ldquo;add 1&rdquo; to the denominator of the &ldquo;IDF&rdquo; 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 -0700 https://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&rsquo;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 &ldquo;quotes.csv&rdquo; 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&amp;f=FIELDS </code></pre><p>In the URL, <code>SYMBOLS</code> is a list of symbols separated by &ldquo;<code>+</code>&rdquo; 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 &amp; 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">&#34;https://finance.yahoo.com/d/quotes.csv&#34;</span> &gt;url </span></span><span class="line"><span class="cl"> <span class="nb">swap </span><span class="s">&#34;+&#34;</span> <span class="nb">join </span><span class="s">&#34;s&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> <span class="s">&#34;sbal1v&#34;</span> <span class="s">&#34;f&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> http-get <span class="nb">nip &gt;string </span>string&gt;csv </span></span><span class="line"><span class="cl"> { <span class="s">&#34;Symbol&#34;</span> <span class="s">&#34;Bid&#34;</span> <span class="s">&#34;Ask&#34;</span> <span class="s">&#34;Last&#34;</span> <span class="s">&#34;Volume&#34;</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">&#34;MSFT&#34;</span> <span class="s">&#34;GOOG&#34;</span> <span class="s">&#34;AAPL&#34;</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 &ldquo;table.csv&rdquo; 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 &rsquo;d&rsquo; for daily (the default), &lsquo;w&rsquo; for weekly, and &rsquo;m&rsquo; 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">&#34;https://ichart.finance.yahoo.com/table.csv&#34;</span> &gt;url </span></span><span class="line"><span class="cl"> <span class="nb">swap </span><span class="s">&#34;s&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> <span class="s">&#34;0&#34;</span> <span class="s">&#34;a&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> <span class="s">&#34;1&#34;</span> <span class="s">&#34;b&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> <span class="s">&#34;2009&#34;</span> <span class="s">&#34;c&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>string&gt;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&amp;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 -0700 https://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 &ldquo;worst algorithm in the world&rdquo; 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">&gt;= </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">&gt;= </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">&gt;= </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">&gt;= </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>&gt;base [ <span class="sc">CHAR: 1 </span><span class="nb">= </span>] { } <span class="nb">map-as </span>:&gt; bits </span></span><span class="line"><span class="cl"> <span class="m">1 </span>:&gt; a! <span class="m">0 </span>:&gt; b! <span class="m">1 </span>:&gt; 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 &ldquo;magical&rdquo; 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&rsquo;s worth pointing out that Factor is about as fast as Python 2.6 for the &ldquo;magical&rdquo; 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 -0700 https://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&rsquo;s. Today, I&rsquo;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&#34; https://ajax.googleapis.com/ajax/services/search/web&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;1.0&#34;</span> <span class="s">&#34;v&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> <span class="nb">swap </span><span class="s">&#34;q&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> <span class="s">&#34;8&#34;</span> <span class="s">&#34;rsz&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> <span class="s">&#34;0&#34;</span> <span class="s">&#34;start&#34;</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">&#34;responseData&#34;</span> <span class="s">&#34;results&#34;</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>&gt;url H{ </span></span><span class="line"><span class="cl"> { font-name <span class="s">&#34;monospace&#34;</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&rsquo;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">&#34;Search results for &#39;%s&#39;&#34;</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&gt;&gt; write-title ] </span></span><span class="line"><span class="cl"> [ content&gt;&gt; write-content ] </span></span><span class="line"><span class="cl"> [ unescapedUrl&gt;&gt; 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 &ldquo;factor&rdquo;):</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 -0700 https://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&rsquo;t figure out how to make URLs &ldquo;clickable&rdquo; 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 &ldquo;capabilities&rdquo; 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 &ldquo;primary operation&rdquo; triggered when the URL is left-clicked, you can add <code>{ +primary+ t }</code> to the hashtable used in the operation&rsquo;s definition.</em></p> Reddit Stats https://re.factorcode.org/2011/05/reddit-stats.html Sun, 01 May 2011 19:49:00 -0700 https://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 &ldquo;Top&rdquo;</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 &ndash; up votes minus down votes &ndash; 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 &ldquo;data&rdquo; 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">&#34;data&#34;</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 &ldquo;pages&rdquo; (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"> &gt;url <span class="nb">dup </span>http-get <span class="nb">nip </span>json&gt; <span class="s">&#34;data&#34;</span> <span class="nb">swap at </span>{ </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;children&#34;</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">&#34;before&#34;</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">&#34;after&#34;</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 &ldquo;paging&rdquo; 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ url&gt;&gt; ] [ after&gt;&gt; <span class="s">&#34;after&#34;</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&gt;&gt; , ] [ <span class="nb">dup </span>after&gt;&gt; ] <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&rsquo;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">&#34;https://api.reddit.com/domain/%s&#34;</span> sprintf json-page all-pages </span></span><span class="line"><span class="cl"> [ created&gt;&gt; <span class="m">1000 </span><span class="nb">* </span>millis&gt;timestamp year&gt;&gt; ] group-by </span></span><span class="line"><span class="cl"> [ [ score&gt;&gt; ] <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"> &#39;[ 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 &gt;alist </span>sort-keys &lt;bar&gt; <span class="m">160 </span>&gt;&gt;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 -0700 https://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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ </span></span><span class="line"><span class="cl"> [ file-info modified&gt;&gt; ] 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">&#34;Library/Application Support/MobileSync/Backup&#34;</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">&#34;3d0d7e5fb2ce288813306e4d4636395e047a3d28&#34;</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&rsquo;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">&lt;copy-sqlite-db&gt;</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 ] [ &lt;sqlite-db&gt; ] <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 &lt;copy-sqlite-db&gt; ] <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">&#34;select * from message&#34;</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">&#34;select count(*) from message&#34;</span> sql-query </span></span><span class="line"><span class="cl"> <span class="nb">first first </span>string&gt;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">&#34;message&#34;</span> { </span></span><span class="line"><span class="cl"> { <span class="s">&#34;rowid&#34;</span> <span class="s">&#34;ROWID&#34;</span> +db-assigned-id+ } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;address&#34;</span> <span class="s">&#34;address&#34;</span> TEXT } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;date&#34;</span> <span class="s">&#34;date&#34;</span> INTEGER } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;text&#34;</span> <span class="s">&#34;text&#34;</span> TEXT } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;flags&#34;</span> <span class="s">&#34;flags&#34;</span> INTEGER } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;replace&#34;</span> <span class="s">&#34;replace&#34;</span> INTEGER } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;svc-center&#34;</span> <span class="s">&#34;svc_center&#34;</span> TEXT } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;group-id&#34;</span> <span class="s">&#34;group_id&#34;</span> INTEGER } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;association-id&#34;</span> <span class="s">&#34;association_id&#34;</span> INTEGER } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;height&#34;</span> <span class="s">&#34;height&#34;</span> INTEGER } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;ui-flags&#34;</span> <span class="s">&#34;UIFlags&#34;</span> INTEGER } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;version&#34;</span> <span class="s">&#34;version&#34;</span> INTEGER } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;subject&#34;</span> <span class="s">&#34;subject&#34;</span> TEXT } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;country&#34;</span> <span class="s">&#34;country&#34;</span> TEXT } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;headers&#34;</span> <span class="s">&#34;headers&#34;</span> BLOB } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;recipients&#34;</span> <span class="s">&#34;recipients&#34;</span> BLOB } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;read?&#34;</span> <span class="s">&#34;read&#34;</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&rsquo;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&rsquo;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>&gt;&gt;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&gt;&gt; ] 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 -0700 https://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> &lt;email&gt; </span></span><span class="line"><span class="cl"> { <span class="s">&#34;[email protected]&#34;</span> } &gt;&gt;to </span></span><span class="line"><span class="cl"> <span class="s">&#34;Up for lunch?&#34;</span> &gt;&gt;subject </span></span><span class="line"><span class="cl"> <span class="s">&#34;At Tracy&#39;s.&#34;</span> &gt;&gt;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&rsquo;s nice, but wouldn&rsquo;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&rsquo;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 &ldquo;To:&rdquo; 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> &lt; <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&gt;&gt; <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">&lt;to&gt;</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&gt;&gt; <span class="s">&#34;To:&#34;</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">&lt;subject&gt;</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&gt;&gt; <span class="s">&#34;Subject:&#34;</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">&lt;body&gt;</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&gt;&gt; &lt;scroller&gt; COLOR: gray &lt;solid&gt; &gt;&gt;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 &ldquo;Send&rdquo; and &ldquo;Cancel&rdquo; 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"> &lt;email&gt; </span></span><span class="line"><span class="cl"> <span class="nb">over </span>to&gt;&gt; editor-string <span class="nb">1array </span>&gt;&gt;to </span></span><span class="line"><span class="cl"> <span class="nb">over </span>subject&gt;&gt; editor-string &gt;&gt;subject </span></span><span class="line"><span class="cl"> <span class="nb">over </span>body&gt;&gt; editor-string &gt;&gt;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">&#34;toolbar&#34;</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">&lt;mail-gadget&gt;</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>&gt;&gt;fill </span></span><span class="line"><span class="cl"> { <span class="m">10 10 </span>} &gt;&gt;gap </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> &lt;editor&gt; &gt;&gt;to </span></span><span class="line"><span class="cl"> &lt;editor&gt; &gt;&gt;subject </span></span><span class="line"><span class="cl"> &lt;multiline-editor&gt; </span></span><span class="line"><span class="cl"> <span class="m">10 </span>&gt;&gt;min-rows </span></span><span class="line"><span class="cl"> <span class="m">60 </span>&gt;&gt;min-cols </span></span><span class="line"><span class="cl"> &gt;&gt;body </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nb">dup </span>&lt;to <span class="no">f </span>track-add </span></span><span class="line"><span class="cl"> <span class="nb">dup </span>&lt;subject&gt; <span class="no">f </span>track-add </span></span><span class="line"><span class="cl"> <span class="nb">dup </span>&lt;body&gt; <span class="m">1 </span>track-add </span></span><span class="line"><span class="cl"> <span class="nb">dup </span>&lt;toolbar&gt; <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 &ldquo;compose&rdquo; 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"> &lt;mail-gadget&gt; </span></span><span class="line"><span class="cl"> { <span class="m">5 5 </span>} &lt;border&gt; { <span class="m">1 1 </span>} &gt;&gt;fill </span></span><span class="line"><span class="cl"> <span class="s">&#34;Compose&#34;</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&rsquo;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 &ldquo;To:&rdquo; and &ldquo;Subject:&rdquo; text fields.</li> <li><a href="https://github.com/mrjbq7/re-factor/commit/1c6136acef6fe76f63923935d43665a3d1004315">Support multiple &ldquo;To:&rdquo; addresses.</a></li> <li>Prompt &ldquo;Are you sure?&rdquo; before closing the window when clicking &ldquo;Cancel&rdquo;.</li> <li><a href="https://github.com/mrjbq7/re-factor/commit/2e7ea44dcbe9d2357993f0adb1892777d4ace845">Don&rsquo;t close the Listener when clicking buttons (with &ldquo;<code>gadget.</code>&rdquo;).</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 -0700 https://re.factorcode.org/2011/04/xkcd.html <p>Pretty much everyone loves Randall Munroe&rsquo;s <a href="https://xkcd.com">XKCD</a> comic. But, wouldn&rsquo;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&#34; https://imgs\.xkcd\.com/comics/[^\.]+\.(png|jpg)&#34;</span> </span></span><span class="line"><span class="cl"> first-match <span class="nb">&gt;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">&#34;https://xkcd.com&#34;</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">&#34;https://xkcd.com/%s/&#34;</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">&#34;https://dynamic.xkcd.com/random/comic/&#34;</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 -0700 https://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 &ldquo;text record&rdquo;, 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 &ldquo;computer&rdquo;:</p> <pre tabindex="0"><code>$ host -t txt computer.wp.dg.cx computer.wp.dg.cx descriptive text &#34;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&#34; &#34; 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&#34; </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 &ldquo;pure Factor&rdquo; 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">&#34;microsoft.com&#34;</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 &ldquo;Wikipedia over DNS&rdquo; 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">&#34;computer.wp.dg.cx&#34;</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 -0700 https://re.factorcode.org/2011/04/group-by.html <p>When dealing with sequences, it can be useful to &ldquo;group&rdquo; them based on some criteria into smaller sequences. I couldn&rsquo;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>&lt;iota&gt; [ 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">&#34;abc&#34;</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">&#34;&#34;</span> } } </span></span><span class="line"><span class="cl"> { <span class="m">1 </span>V{ <span class="s">&#34;a&#34;</span> <span class="s">&#34;b&#34;</span> <span class="s">&#34;c&#34;</span> } } </span></span><span class="line"><span class="cl"> { <span class="m">2 </span>V{ <span class="s">&#34;ab&#34;</span> <span class="s">&#34;ac&#34;</span> <span class="s">&#34;bc&#34;</span> } } </span></span><span class="line"><span class="cl"> { <span class="m">3 </span>V{ <span class="s">&#34;abc&#34;</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&rsquo;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 &ldquo;<code>;</code>&rdquo;. 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&rsquo;t &ldquo;consumed&rdquo;, 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 -0700 https://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&rsquo;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 &ldquo;variable length string full of random characters&rdquo;. 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 &ldquo;random string&rdquo; 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">&lt;</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">&gt;</span> <span class="m">65</span> <span class="p">&amp;&amp;</span> <span class="n">n</span> <span class="p">&lt;</span> <span class="m">90</span> <span class="p">||</span> <span class="n">n</span> <span class="p">&gt;</span> <span class="m">97</span> <span class="p">&amp;&amp;</span> <span class="n">n</span> <span class="p">&lt;</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 &ldquo;main&rdquo; 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">&#34;&#34;</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 -0700 https://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>&ldquo;Write a function to determine if a number is a power of 2.&rdquo;</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&rsquo;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">&lt;= </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 &ldquo;on&rdquo; 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">&lt;= </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">&lt;= </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&rsquo;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">&lt;= </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">&lt;= </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>&lt;iota&gt; [ <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">&gt;= </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">&lt;= </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>&#39;[ </span></span><span class="line"><span class="cl"> [ name&gt;&gt; <span class="s">&#34;/&#34;</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&gt;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">&#34;log2&#34;</span> <span class="m">118107290 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;complement&#34;</span> <span class="m">119691428 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;decrement&#34;</span> <span class="m">121455742 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;log-search&#34;</span> <span class="m">122799186 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;next-power&#34;</span> <span class="m">127366447 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;shift-right&#34;</span> <span class="m">137695485 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;binary-search&#34;</span> <span class="m">204224141 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;check-all&#34;</span> <span class="m">267042396 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;hash-search&#34;</span> <span class="m">269629705 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;linear-search&#34;</span> <span class="m">280441186 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;bits&#34;</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 &ldquo;decrement&rdquo;. 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">&gt; </span>] } 1&amp;&amp; <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">&lt;= </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">&lt;stdint.h&gt;</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">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="p">((</span><span class="n">x</span> <span class="o">&amp;</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">&#34;libpowerof2&#34;</span> <span class="s">&#34;power-of-2.dylib&#34;</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">&#34;decrement+typed&#34;</span> <span class="m">111711456 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;decrement+short&#34;</span> <span class="m">112070520 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;decrement+alien&#34;</span> <span class="m">113014058 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;decrement&#34;</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&rsquo;d be happy declaring <code>decrement+short/power-of-2?</code> the &ldquo;winner&rdquo;. 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 -0700 https://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&rsquo;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">&#34;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz&#34;</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">&gt; </span>] [ <span class="m">62 </span><span class="nb">/mod </span>CHARS <span class="nb">nth </span>] <span class="s">&#34;&#34;</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 &ldquo;secret&rdquo; 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 &ldquo;unique hash&rdquo; 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&rsquo;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 -0700 https://re.factorcode.org/2011/03/sum.html <p>Today&rsquo;s <a href="https://programmingpraxis.com/2011/03/25/sum/">programming challenge</a> is to implement the &ldquo;old Unix Sys V R4&rdquo; <code>sum</code> command:</p> <blockquote> <p><em>&ldquo;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.&rdquo;</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">&#34;%d %d %s\n&#34;</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">&#34;/usr/share/dict/words&#34;</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">&#34;%d %d %s\n&#34;</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">&#34;%s: not found\n&#34;</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">&#34;&#34;</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 -0700 https://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 &ldquo;Look and Say&rdquo; 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> number&gt;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">&#34;%d%c&#34;</span> sprintf </span></span><span class="line"><span class="cl"> ] <span class="nb">map concat </span>string&gt;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 -0700 https://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">&#34;Gadaffi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Gadafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Gadafy&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Gaddafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Gaddafy&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Gaddhafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Gadhafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Gathafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Ghadaffi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Ghadafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Ghaddafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Ghaddafy&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Gheddafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Kadaffi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Kadafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Kad&#39;afi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Kaddafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Kadhafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Kazzafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Khadaffy&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Khadafy&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Khaddafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Qadafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Qaddafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Qadhafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Qadhaafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Qadhdhafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Qadthafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Qathafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Quathafi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Qudhafi&#34;</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">&#34;|&#34;</span> <span class="nb">join </span>&lt;regexp&gt; ] 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&rsquo;t take into account that sometimes he is called &ldquo;Al Qaddafi&rdquo; or &ldquo;el-Gaddafi&rdquo;. 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[&#39;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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> names-pattern <span class="s">&#34;Gaddafi&#34;</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">&#34;G310&#34;</span> <span class="s">&#34;K310&#34;</span> <span class="s">&#34;Q310&#34;</span> <span class="s">&#34;Q331&#34;</span> } <span class="nb">member? </span><span class="k">; </span></span></span></code></pre></div><p>Some disadvantages of this is that it doesn&rsquo;t capture names with prefix &ldquo;Al&rdquo; or &ldquo;El&rdquo;, and misses some names (e.g., &ldquo;Kazzafi&rdquo; has a soundex value of &ldquo;K210&rdquo;, but that would produce a false match against a name like &ldquo;KOSOFF&rdquo;).</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&rsquo;ammar, Mu&rsquo;amar, Moamma, etc.), and include text that is &ldquo;<code>FIRSTNAME LASTNAME</code>&rdquo; or &ldquo;<code>LASTNAME, FIRSTNAME</code>&rdquo;.</p> Typed Netstrings https://re.factorcode.org/2011/03/typed-netstrings.html Sun, 20 Mar 2011 14:24:00 -0700 https://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&rsquo;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 &ldquo;<code>{LENGTH}:{PAYLOAD}{TYPE}</code>&rdquo;. 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">&#34;:&#34;</span> split1 <span class="nb">swap </span>string&gt;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 &ldquo;typed netstrings&rdquo; (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&gt;number ] } </span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: &#34; </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">&#34;Invalid payload type: %c&#34;</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">&#34;Unbalanced dictionary store&#34;</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">&#34;Invalid value, null not allowed&#34;</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>&gt;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">&#34;Had trailing junk: %s&#34;</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&rsquo;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;34:5:hello\&#34;22:11:12345678901#4:this\&#34;]}&#34;</span> tnetstring <span class="m">. </span></span></span><span class="line"><span class="cl">H{ { <span class="s">&#34;hello&#34;</span> { <span class="m">12345678901 </span><span class="s">&#34;this&#34;</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 -0700 https://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&rsquo;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 -0700 https://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&#34; https://www.googleapis.com/language/translate/v2&#34;</span> </span></span><span class="line"><span class="cl"> google-api-key <span class="nb">get-global </span><span class="s">&#34;key&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> source <span class="s">&#34;source&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> target <span class="s">&#34;target&#34;</span> set-query-param </span></span><span class="line"><span class="cl"> text <span class="s">&#34;q&#34;</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&#39;</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">&#34;data&#34;</span> <span class="s">&#34;translations&#34;</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">&#34;translatedText&#34;</span> <span class="nb">swap at </span><span class="k">; </span></span></span></code></pre></div><p>Once you&rsquo;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">&#34;Hello world!&#34;</span> <span class="s">&#34;en&#34;</span> <span class="s">&#34;es&#34;</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&rsquo;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">&ldquo;Type anything here and you&rsquo;ll get funny&rdquo;</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">&#34;Type anything here and you&#39;ll get funny&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;en&#34;</span> <span class="s">&#34;ja&#34;</span> translation-party </span></span><span class="line"><span class="cl">Type anything here <span class="nb">and </span>you&#39;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 -0800 https://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&gt;&gt; ] [ usage <span class="nb">length </span>] <span class="nb">bi </span>] { } <span class="nb">map&gt;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 &ldquo;data-intense, design-simple, word-sized graphics&rdquo;. 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 -0800 https://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 &ldquo;actual&rdquo; 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 &ldquo;actual&rdquo; 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&rsquo;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 &gt; </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 -0800 https://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&rsquo;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>&#39;sysv&#39;, </span></span><span class="line"><span class="cl"> gestaltSystemVersionMajor <span class="nb">= </span>&#39;sys1&#39;, </span></span><span class="line"><span class="cl"> gestaltSystemVersionMinor <span class="nb">= </span>&#39;sys2&#39;, </span></span><span class="line"><span class="cl"> gestaltSystemVersionBugFix <span class="nb">= </span>&#39;sys3&#39; </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">&#34;sysv&#34;</span> be&gt; 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">&#34;sys1&#34;</span> be&gt; 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">&#34;sys2&#34;</span> be&gt; 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">&#34;sys3&#34;</span> be&gt; 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&#39;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&rsquo;s &ldquo;code name&rdquo; 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">&#34;Lion&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x1060 </span>[ <span class="s">&#34;Snow Leopard&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x1050 </span>[ <span class="s">&#34;Leopard&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x1040 </span>[ <span class="s">&#34;Tiger&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x1030 </span>[ <span class="s">&#34;Panther&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x1020 </span>[ <span class="s">&#34;Jaguar&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x1010 </span>[ <span class="s">&#34;Puma&#34;</span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x1000 </span>[ <span class="s">&#34;Cheetah&#34;</span> ] } </span></span><span class="line"><span class="cl"> [ <span class="nb">drop </span><span class="s">&#34;Unknown&#34;</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">&#34;%s.%s.%s&#34;</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">&#34;%s (%s)\n&#34;</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">&#34;/System/Library/CoreServices/SystemVersion.plist&#34;</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">&#34;ProductVersion&#34;</span> <span class="s">&#34;10.6.6&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;ProductName&#34;</span> <span class="s">&#34;Mac OS&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;ProductBuildVersion&#34;</span> <span class="s">&#34;10J567&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;ProductUserVisibleVersion&#34;</span> <span class="s">&#34;10.6.6&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;ProductCopyright&#34;</span> <span class="s">&#34;1983-2011 Apple Inc.&#34;</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 -0800 https://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 &ldquo;inventor&rdquo; of port knocking.</p> <blockquote> <p><em>&quot;<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&quot;</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"> &lt;inet&gt; [ 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 &ldquo;Hello&rdquo; 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">&#34;localhost&#34;</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>&lt;inet&gt; ascii [ <span class="s">&#34;Hello&#34;</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 &ldquo;knock delay&rdquo; 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 -0800 https://re.factorcode.org/2011/02/port-scanner.html <p>For fun, I thought I&rsquo;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"> &lt;inet&gt; [ 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&rsquo;m not quite sure how to do that &ndash; the documentation for <code>io.sockets</code> and <code>io.timeouts</code> didn&rsquo;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">&#34;Scanning %s...\n&#34;</span> printf ] </span></span><span class="line"><span class="cl"> [ open-ports [ <span class="s">&#34;%d is open\n&#34;</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">&#34;127.0.0.1&#34;</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 -0800 https://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 &ldquo;fluent interfaces&rdquo; 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 &ldquo;fluent&rdquo;, 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 &ldquo;simple RPG&rdquo; 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&gt; 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&rsquo;ll only mention in passing. The &ldquo;fluent&rdquo; 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">&#34;King&#34;</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">&#34;King&#34;</span> &gt;&gt;name </span></span><span class="line"><span class="cl"> <span class="s">&#34;fighter&#34;</span> &gt;&gt;class </span></span><span class="line"><span class="cl"> <span class="m">49 </span>&gt;&gt;age </span></span><span class="line"><span class="cl"> <span class="m">50 </span>&gt;&gt;hp </span></span><span class="line"><span class="cl"> <span class="m">17 </span>&gt;&gt;strength </span></span><span class="line"><span class="cl"> <span class="m">12 </span>&gt;&gt;agility </span></span><span class="line"><span class="cl"> <span class="m">15 </span>&gt;&gt;intelligence </span></span><span class="line"><span class="cl"> <span class="m">9999999 </span>&gt;&gt;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">&#34;fighter&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;mage&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;cleric&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;rogue&#34;</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&gt;&gt; <span class="m">0 </span><span class="nb">&gt; </span><span class="k">; </span></span></span></code></pre></div><p>We can print out our character&rsquo;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&gt;&gt; <span class="s">&#34;Name: %s\n&#34;</span> printf ] </span></span><span class="line"><span class="cl"> [ class&gt;&gt; <span class="s">&#34;Class: %s\n&#34;</span> printf ] </span></span><span class="line"><span class="cl"> [ [ hp&gt;&gt; ] [ max-hp&gt;&gt; ] <span class="nb">bi </span><span class="s">&#34;HP: %d/%d\n&#34;</span> printf ] </span></span><span class="line"><span class="cl"> [ age&gt;&gt; <span class="s">&#34;Age: %d\n&#34;</span> printf ] </span></span><span class="line"><span class="cl"> [ </span></span><span class="line"><span class="cl"> [ str&gt;&gt; ] [ agi&gt;&gt; ] [ int&gt;&gt; ] <span class="nb">tri </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;Str %d / Agi %d / Int %d\n&#34;</span> printf </span></span><span class="line"><span class="cl"> ] </span></span><span class="line"><span class="cl"> [ gold&gt;&gt; <span class="s">&#34;Gold: %d\n&#34;</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 &ldquo;quick&rdquo; 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&gt;&gt; ] [ hp&gt;&gt; ] [ max-hp&gt;&gt; ] <span class="nb">tri </span><span class="s">&#34;%s (%d/%d)&#34;</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">&#34;slashes&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;stabs&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;smashes&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;impales&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;poisons&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;shoots&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;incinerates&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;destroys&#34;</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 :&gt; damage </span></span><span class="line"><span class="cl"> attacker name&gt; </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&gt; </span></span><span class="line"><span class="cl"> damage </span></span><span class="line"><span class="cl"> <span class="s">&#34;%s %s %s for %d damage!\n&#34;</span> printf <span class="k">; </span></span></span></code></pre></div><p>The main battle logic starts a battle, loops performing a &ldquo;fight to the death&rdquo;, 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">&#34;= Starting Battle =&#34;</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">&#34;vs.&#34;</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">&#34;An enemy approaches&gt; &#34;</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">&#34; / &#34;</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">&#34;&gt; &#34;</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">&#34;Our hero survives to fight another battle! &#34;</span> <span class="nb">write </span></span></span><span class="line"><span class="cl"> enemy gold&gt;&gt; <span class="s">&#34;Won %d gold!\n&#34;</span> printf </span></span><span class="line"><span class="cl"> hero [ enemy gold&gt;&gt; <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&gt;&gt; <span class="s">&#34;Our hero has fallen with %d gold! &#34;</span> printf </span></span><span class="line"><span class="cl"> <span class="s">&#34;The world is covered in darkness once again.&#34;</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 &ldquo;Valient&rdquo;, 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">&lt;hero&gt;</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">&#34;Valient&#34;</span> &gt;&gt;name </span></span><span class="line"><span class="cl"> <span class="s">&#34;fighter&#34;</span> &gt;&gt;class </span></span><span class="line"><span class="cl"> <span class="m">22 </span>&gt;&gt;age </span></span><span class="line"><span class="cl"> <span class="m">20 </span>[ &gt;&gt;hp ] [ &gt;&gt;max-hp ] <span class="nb">bi </span></span></span><span class="line"><span class="cl"> <span class="m">18 </span>&gt;&gt;str </span></span><span class="line"><span class="cl"> <span class="m">14 </span>&gt;&gt;agi </span></span><span class="line"><span class="cl"> <span class="m">12 </span>&gt;&gt;int </span></span><span class="line"><span class="cl"> <span class="m">0 </span>&gt;&gt;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">&#34;Destro&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Victo&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Mozri&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Fang&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Ovi&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Hell&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Syth&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;End&#34;</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">&#34;math&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;rin&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;sith&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;icous&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;ravage&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;wrath&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;ryn&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;less&#34;</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">&lt;enemy&gt;</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 &gt;&gt;name </span></span><span class="line"><span class="cl"> classes random &gt;&gt;class </span></span><span class="line"><span class="cl"> <span class="m">12 200 </span>[a..b] random &gt;&gt;age </span></span><span class="line"><span class="cl"> <span class="m">5 12 </span>[a..b] random [ &gt;&gt;hp ] [ &gt;&gt;max-hp ] <span class="nb">bi </span></span></span><span class="line"><span class="cl"> <span class="m">21 </span>[1..b) random &gt;&gt;str </span></span><span class="line"><span class="cl"> <span class="m">21 </span>[1..b) random &gt;&gt;agi </span></span><span class="line"><span class="cl"> <span class="m">21 </span>[1..b) random &gt;&gt;int </span></span><span class="line"><span class="cl"> <span class="m">50 </span>random &gt;&gt;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"> &lt;hero&gt; [ <span class="nb">dup </span>alive? ] [ <span class="nb">dup </span>&lt;enemy&gt; 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 -0800 https://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 &ldquo;ping&rdquo; utility in <a href="https://www.factorcode.org">Factor</a>. Doug Coleman helped get it working on Windows. Below I&rsquo;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 &ldquo;host/port&rdquo; tuple in IPv4 and IPv6, respectively. ICMP is similar in that it requires a &ldquo;host&rdquo;, but the &ldquo;port&rdquo; is unnecessary. With Slava&rsquo;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 &ldquo;Echo&rdquo; and &ldquo;Echo Reply&rdquo; packets are described. Both have the same form (except that &ldquo;Echo&rdquo; is type 8 and &ldquo;Echo Reply&rdquo; is type 0). We define an <code>echo</code> tuple to represent both types. The <code>&lt;echo&gt;</code> constructor is then used to create an &ldquo;Echo&rdquo; (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">&lt;echo&gt;</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>&rsquo;s and <code>byte-array</code>&rsquo;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&gt;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&gt;&gt; <span class="m">0 0 </span>] <span class="c">! code checksum</span> </span></span><span class="line"><span class="cl"> [ identifier&gt;&gt; ] </span></span><span class="line"><span class="cl"> [ sequence&gt;&gt; ] <span class="nb">tri </span></span></span><span class="line"><span class="cl"> ] output&gt;array <span class="s">&#34;CCSSS&#34;</span> pack-be </span></span><span class="line"><span class="cl"> ] [ data&gt;&gt; ] <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&gt;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">&#34;CCSSS&#34;</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>{ } &lt;echo&gt; echo&gt;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 &ndash; 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&gt;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&rsquo;s a subtle bug where we set a &ldquo;read timeout&rdquo; 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 &ldquo;raw&rdquo; 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">&ldquo;man 4 icmp&rdquo;</a> shows you something called &ldquo;Non-privileged ICMP&rdquo; which allows you to create an ICMP socket using the &ldquo;datagram&rdquo; 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">&lt;ping-port&gt;</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">&lt;ping-port&gt;</span> &lt;raw&gt; <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">&lt;ping-port&gt;</span> &lt;datagram&gt; <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"> &lt;icmp&gt; resolve-host [ icmp4? ] <span class="nb">filter </span>random </span></span><span class="line"><span class="cl"> <span class="no">f </span>&lt;icmp4&gt; &lt;ping-port&gt; </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">&#34;127.0.0.1&#34;</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 &ldquo;pseudo-header&rdquo; in the checksum calculation).</p> Maximum/Minimum https://re.factorcode.org/2011/02/maximum-minimum.html Fri, 04 Feb 2011 14:06:00 -0800 https://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 &ldquo;maximum&rdquo; and &ldquo;minimum&rdquo; of some sequences of values. The &ldquo;obvious&rdquo; 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&rsquo;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&rsquo;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">&#34;Jim&#34;</span> <span class="m">30 </span>} </span></span><span class="line"><span class="cl"> T{ person <span class="no">f </span><span class="s">&#34;Sally&#34;</span> <span class="m">27 </span>} </span></span><span class="line"><span class="cl"> T{ person <span class="no">f </span><span class="s">&#34;Rebecca&#34;</span> <span class="m">32 </span>} </span></span><span class="line"><span class="cl"> T{ person <span class="no">f </span><span class="s">&#34;James&#34;</span> <span class="m">28 </span>} </span></span><span class="line"><span class="cl"> T{ person <span class="no">f </span><span class="s">&#34;Benjamin&#34;</span> <span class="m">22 </span>} </span></span><span class="line"><span class="cl">} </span></span></code></pre></div><p>What if we&rsquo;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 &lt;=&gt; 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">&#34;Sally&#34;</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>&lt;=&gt;</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&rsquo;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">&lt;=&gt;</span> [ age&gt;&gt; ] <span class="nb">bi@ </span>&lt;=&gt; <span class="k">; </span></span></span></code></pre></div><p>Okay, let&rsquo;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">&#34;Rebecca&#34;</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>&lt;=&gt;</code> which (from our previous definition) currently orders by a person&rsquo;s <code>age</code>. Let&rsquo;s redefine <code>&lt;=&gt;</code> to compare based on length of a person&rsquo;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">&lt;=&gt;</span> [ name&gt;&gt; <span class="nb">length </span>] <span class="nb">bi@ </span>&lt;=&gt; <span class="k">; </span></span></span></code></pre></div><p>Okay, let&rsquo;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">&#34;Jim&#34;</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>&lt;=&gt;</code> for each purpose. The problem here is that <code>supremum</code> and <code>infimum</code> (and the <code>&lt;=&gt;</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 &ldquo;maximum&rdquo; or &ldquo;minimum&rdquo; 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&gt;&gt; ] maximum <span class="m">. </span></span></span><span class="line"><span class="cl">T{ person <span class="no">f </span><span class="s">&#34;Rebecca&#34;</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&gt;&gt; <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">&#34;Jim&#34;</span> <span class="m">30 </span>} </span></span></code></pre></div><p>It&rsquo;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&rsquo;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 -0800 https://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 &ldquo;calculation engine&rdquo; 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 &ldquo;free&rdquo; 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">&#34;https://api.wolframalpha.com/v2/query?input=%s&amp;appid=%s&#34;</span> </span></span><span class="line"><span class="cl"> sprintf http-get <span class="nb">nip </span>string&gt;xml <span class="k">; </span></span></span></code></pre></div><p>Inside the XML response are &ldquo;pods&rdquo; corresponding to the different types of information returned by Wolfram|Alpha. You can choose to receive the &ldquo;pods&rdquo; 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 &ldquo;pods&rdquo;, outputs the title and images contained in each &ldquo;pod&rdquo;:</p> <div class="highlight"><pre 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">&#34;pod&#34;</span> tags-named [ </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;title&#34;</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">&#34;img&#34;</span> deep-tags-named </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;src&#34;</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">&#34;XXXXXX-XXXXXXXXXX&#34;</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&rsquo;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 -0800 https://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">&#34;https://github.com/api/v2/json/user/show/seejohnrun&#34;</span> </span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>json&gt; <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">&#34;user&#34;</span> </span></span><span class="line"><span class="cl"> H{ </span></span><span class="line"><span class="cl"> { <span class="s">&#34;followers_count&#34;</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">&#34;gravatar_id&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;3a0541ed3d5324bb54b9f07990be20ae&#34;</span> </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;login&#34;</span> <span class="s">&#34;seejohnrun&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;public_gist_count&#34;</span> <span class="m">0 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;public_repo_count&#34;</span> <span class="m">23 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;location&#34;</span> <span class="s">&#34;Verona, NJ&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;created_at&#34;</span> <span class="s">&#34;2009/03/19 10:29:18 -0700&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;type&#34;</span> <span class="s">&#34;User&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;id&#34;</span> <span class="m">64965 </span>} </span></span><span class="line"><span class="cl"> { <span class="s">&#34;blog&#34;</span> <span class="s">&#34;https://johncrepezzi.com&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;email&#34;</span> <span class="s">&#34;[email protected]&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;name&#34;</span> <span class="s">&#34;John Crepezzi&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;company&#34;</span> <span class="s">&#34;Patch&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;permission&#34;</span> json-null } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;following_count&#34;</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 &ldquo;Top&rdquo;</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">&#34;https://github.com/api/v2/json/user/show/%s&#34;</span> sprintf </span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>json&gt; <span class="s">&#34;user&#34;</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">&#34;https://github.com/api/v2/json/repos/show/%s&#34;</span> sprintf </span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>json&gt; <span class="s">&#34;repositories&#34;</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&gt;&gt; ] </span></span><span class="line"><span class="cl"> [ followers_count&gt;&gt; ] </span></span><span class="line"><span class="cl"> [ public_repo_count&gt;&gt; ] </span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;%s - %s followers - %s public repositories\n&#34;</span> printf </span></span><span class="line"><span class="cl"> ] [ </span></span><span class="line"><span class="cl"> repositories [ watchers&gt;&gt; ] inv-sort-with [ </span></span><span class="line"><span class="cl"> { </span></span><span class="line"><span class="cl"> [ name&gt;&gt; ] </span></span><span class="line"><span class="cl"> [ watchers&gt;&gt; <span class="s">&#34;%s watchers&#34;</span> sprintf ] </span></span><span class="line"><span class="cl"> [ forks&gt;&gt; <span class="s">&#34;%s forks&#34;</span> sprintf ] </span></span><span class="line"><span class="cl"> [ fork&gt;&gt; <span class="s">&#34;(FORK)&#34;</span> <span class="s">&#34;&#34;</span> <span class="nb">? </span>] </span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span><span class="s">&#34;%-25s %12s %12s %s\n&#34;</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">&#34;seejohnrun&#34;</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 -0800 https://re.factorcode.org/2011/01/todo-lists.html <p>It seems the thing to do these days is to write &ldquo;a better todo list&rdquo;. 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&rsquo;m thinking: <em>wouldn&rsquo;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">&#34;todo.txt&#34;</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">&#34;%s:\n&#34;</span> printf ] [ [ <span class="s">&#34;- %s\n&#34;</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">&#34;foo&#34;</span> scaffold-work </span></span><span class="line"><span class="cl">Creating scaffolding for <span class="s">P&#34; resource:work/foo/foo.factor&#34;</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">&#34;foo.bar&#34;</span> scaffold-work </span></span><span class="line"><span class="cl">Creating scaffolding for <span class="s">P&#34; resource:work/foo/bar/bar.factor&#34;</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">&#34;The first thing&#34;</span> <span class="s">&#34;foo&#34;</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">&#34;The second thing&#34;</span> <span class="s">&#34;foo&#34;</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">&#34;Another thing&#34;</span> <span class="s">&#34;foo.bar&#34;</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">&#34;foo&#34;</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">&#34;foo&#34;</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">&#34;&#34;</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">&#34;vocab-todos&#34;</span> <span class="s">&#34;Vocabulary todos&#34;</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">&#34;vocab-todos&#34;</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 -0800 https://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 &ldquo;Top&rdquo;</a>, this was something I wanted but couldn&rsquo;t find in <a href="https://www.factorcode.org">Factor</a>. It would be great to have cross-platform &ldquo;open URL&rdquo; 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">&#34;open \&#34;%s\&#34;&#34;</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 &ldquo;open location&rdquo; feature. This is how you might do it, if you want to target a specific browser (&ldquo;tell application&hellip; activate OpenURL&hellip;&rdquo;), indicate that it should open in a new window (&quot;&hellip;toWindow&hellip;&quot;), 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">&#34;osascript&#34;</span> ascii [ </span></span><span class="line"><span class="cl"> url-encode <span class="s">&#34;open location \&#34;%s\&#34;&#34;</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 &ndash; 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">&#34;gnome-open \&#34;%s\&#34;&#34;</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">&#34;open&#34;</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">&#34;https://www.factorcode.org&#34;</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&rsquo;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 -0800 https://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">&#34;https://api.reddit.com/r/programming&#34;</span> </span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>json&gt; <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">&#34;https://api.reddit.com/r/%s&#34;</span> sprintf http-get <span class="nb">nip </span></span></span><span class="line"><span class="cl"> json&gt; { <span class="s">&#34;data&#34;</span> <span class="s">&#34;children&#34;</span> } [ <span class="nb">swap at </span>] <span class="nb">each </span></span></span><span class="line"><span class="cl"> [ <span class="s">&#34;data&#34;</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">&#34;%2d. &#34;</span> printf { </span></span><span class="line"><span class="cl"> [ title&gt;&gt; ] </span></span><span class="line"><span class="cl"> [ url&gt;&gt; ] </span></span><span class="line"><span class="cl"> [ score&gt;&gt; ] </span></span><span class="line"><span class="cl"> [ num_comments&gt;&gt; ] </span></span><span class="line"><span class="cl"> [ </span></span><span class="line"><span class="cl"> created_utc&gt;&gt; unix-time&gt;timestamp now <span class="nb">swap </span>time- </span></span><span class="line"><span class="cl"> duration&gt;hours <span class="s">&#34;%d hours ago&#34;</span> sprintf </span></span><span class="line"><span class="cl"> ] </span></span><span class="line"><span class="cl"> [ author&gt;&gt; ] </span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;%s\n %s\n %d points, %d comments, posted %s by %s\n\n&#34;</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 -0800 https://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">&#34;a&#34;</span> <span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">&#34;b&#34;</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">&#34;a&#34;</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">&#34;b&#34;</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">&#34;c&#34;</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">&#34;a&#34;</span> <span class="m">1 </span>} { <span class="s">&#34;b&#34;</span> <span class="m">2 </span>} } { </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;a&#34;</span> <span class="nb">swap at </span>&gt;&gt;a ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;b&#34;</span> <span class="nb">swap at </span>&gt;&gt;b ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;c&#34;</span> <span class="nb">swap at </span>&gt;&gt;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&gt;&gt; <span class="m">. </span>] [ b&gt;&gt; <span class="m">. </span>] [ c&gt;&gt; <span class="m">. </span>] <span class="nb">tri </span></span></span></code></pre></div><p>But, it&rsquo;s much better if you don&rsquo;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"> &#39;[ <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">&#34;a&#34;</span> <span class="m">1 </span>} { <span class="s">&#34;b&#34;</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 -0800 https://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 &ldquo;packed structure&rdquo; (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 &ndash; since it should be &ldquo;double null terminated&rdquo;), 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&gt;alien B{ <span class="m">0 0 </span>} <span class="nb">append </span></span></span><span class="line"><span class="cl"> malloc-byte-array &amp;free </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> SHFILEOPSTRUCTW &lt;struct&gt; </span></span><span class="line"><span class="cl"> <span class="no">f </span>&gt;&gt;hwnd </span></span><span class="line"><span class="cl"> FO_DELETE &gt;&gt;wFunc </span></span><span class="line"><span class="cl"> <span class="nb">swap </span>&gt;&gt;pFrom </span></span><span class="line"><span class="cl"> <span class="no">f </span>&gt;&gt;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>&gt;&gt;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 -0800 https://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 &ldquo;top directory&rdquo; 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">&#34;..&#34;</span> append-path [ link-status ] <span class="nb">bi@ </span></span></span><span class="line"><span class="cl"> [ [ st_dev&gt;&gt; ] <span class="nb">bi@ = not </span>] [ [ st_ino&gt;&gt; ] <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&#39;</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">&#34;..&#34;</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 &ldquo;user-only&rdquo; 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&gt;&gt; +symbolic-link+ <span class="nb">= not </span>] </span></span><span class="line"><span class="cl"> } 1&amp;&amp; [ <span class="s">&#34;invalid trash path&#34;</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&rsquo;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&rsquo;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">&#34;XDG_DATA_HOME&#34;</span> os-env </span></span><span class="line"><span class="cl"> home <span class="s">&#34;.local/share&#34;</span> append-path <span class="nb">or </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;Trash&#34;</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">&#34;.Trash&#34;</span> append-path <span class="nb">dup </span>check-trash-path </span></span><span class="line"><span class="cl"> real-user-id number&gt;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">&#34;.Trash-%d&#34;</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&#39;</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">&#34;.Trash&#34;</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 &ldquo;safe&rdquo; 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&#39;</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">&#34;.&#34;</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">&#34;%s%s %s%s&#34;</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&#39;</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 &ldquo;information file&rdquo; 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">&#34;files&#34;</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">&#34;info&#34;</span> append-path [ make-user-directory ] <span class="nb">keep </span></span></span><span class="line"><span class="cl"> to-directory <span class="s">&#34;.trashinfo&#34;</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">&#34;[Trash Info]&#34;</span> <span class="nb">write nl </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;Path=&#34;</span> <span class="nb">write write nl </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;DeletionDate=&#34;</span> <span class="nb">write </span></span></span><span class="line"><span class="cl"> now <span class="s">&#34;%Y-%m-%dT%H:%M:%S&#34;</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 -0800 https://re.factorcode.org/2011/01/trashing-files-part-1-mac-os.html <p>Most operating systems provide support for sending files to the &ldquo;trash can&rdquo; (or sometimes &ldquo;recycle bin&rdquo;). Inspired by a python project called <a href="https://www.hardcoded.net/articles/send-files-to-trash-on-all-platforms.htm">&ldquo;send2trash&rdquo;</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">&#34;trash.macosx&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ os unix? ] [ <span class="s">&#34;trash.unix&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ os winnt? ] [ <span class="s">&#34;trash.windows&#34;</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&gt;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>&lt;fs-ref&gt;</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">&lt;fs-ref&gt;</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&gt;alien </span></span><span class="line"><span class="cl"> kFSPathMakeRefDoNotFollowLeafSymlink </span></span><span class="line"><span class="cl"> FSRef &lt;struct&gt; </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 &ldquo;trashing&rdquo; 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"> &lt;fs-ref&gt; <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&rsquo;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">&#34;&#34;</span> <span class="s">&#34;/tmp/foo&#34;</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">&#34;/tmp/foo&#34;</span> send-to-trash </span></span></code></pre></div><p><em>Note: This method does not appear to support the &ldquo;Put Back&rdquo; functionality (to &ldquo;undo&rdquo; 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 -0800 https://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&rsquo;d like to have Factor print <code>&quot;Hello, World!&quot;</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">&#34;Hello, World!&#34;</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&rsquo;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">&#34;formatting&#34;</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&rsquo;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 -0800 https://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 &ldquo;<code>Hello World!</code>&rdquo;.</p> <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">&#34;Hello World!&#34;</span> </span></span></code></pre></div><h3 id="fitness">Fitness</h3> <p>The fitness of a chromosome is the &ldquo;sum of the character-wise differences between the chromosome and the target string&rdquo;.</p> <div class="highlight"><pre 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 &ldquo;creating 400 totally random 12 character strings&rdquo;.</p> <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">&#34;&#34;</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 &ldquo;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&rdquo;.</p> <div class="highlight"><pre 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&#39;</span> <span class="nv">parent2&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="nb">2dup </span>[ fitness ] <span class="nb">bi@ &gt; </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 &ldquo;ensure their genes survive in the next generation&rdquo;. 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">&lt; </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 &ldquo;choose a random position and alter the character that&rsquo;s there by up to 5 places&rdquo;.</p> <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">&lt; </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&#39;</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&#39;</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"> &#39;[ _ [ _ (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 -0800 https://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&rsquo;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">&#34;.byte 0x0f, 0x31&#34;</span> <span class="o">:</span> <span class="s">&#34;=A&#34;</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">&#34;rdtsc&#34;</span> <span class="o">:</span> <span class="s">&#34;=a&#34;</span><span class="p">(</span><span class="n">lo</span><span class="p">),</span> <span class="s">&#34;=d&#34;</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">&lt;&lt;</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 &ldquo;benchmarking&rdquo; 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 &ldquo;busy loop&rdquo; 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">&gt; </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 -0800 https://re.factorcode.org/2010/11/github-supports-factor.html <p><em>I&rsquo;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&rsquo;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 -0700 https://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&rsquo;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&rsquo;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&rsquo;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">&#34;NULL&#34;</span> H{ { foreground COLOR: #000000 } } } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;COMMENT1&#34;</span> H{ { foreground COLOR: #cc0000 } } } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;COMMENT2&#34;</span> H{ { foreground COLOR: #ff8400 } } } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;COMMENT3&#34;</span> H{ { foreground COLOR: #6600cc } } } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;COMMENT4&#34;</span> H{ { foreground COLOR: #cc6600 } } } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;DIGIT&#34;</span> H{ { foreground COLOR: #ff0000 } } } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;FUNCTION&#34;</span> H{ { foreground COLOR: #9966ff } } } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;INVALID&#34;</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">&#34;KEYWORD1&#34;</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">&#34;KEYWORD2&#34;</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">&#34;KEYWORD3&#34;</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">&#34;KEYWORD4&#34;</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">&#34;LABEL&#34;</span> H{ { foreground COLOR: #02b902 } } } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;LITERAL1&#34;</span> H{ { foreground COLOR: #ff00cc } } } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;LITERAL2&#34;</span> H{ { foreground COLOR: #cc00cc } } } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;LITERAL3&#34;</span> H{ { foreground COLOR: #9900cc } } } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;LITERAL4&#34;</span> H{ { foreground COLOR: #6600cc } } } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;MARKUP&#34;</span> H{ { foreground COLOR: #0000ff } } } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;OPERATOR&#34;</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 &ldquo;mode&rdquo;) 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&gt;&gt; ] [ id&gt;&gt; ] <span class="nb">bi </span></span></span><span class="line"><span class="cl"> [ name&gt;&gt; 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 -0700 https://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 &#39;https://yqlblog.net/samples/data.html.cssselect.xml&#39; as data.html.cssselect; select * from data.html.cssselect where url=&#34;repopular.com&#34; and css=&#34;div.pad a&#34; </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">&#34;https://query.yahooapis.com/v1/public/yql?q=use%20&#39;http%3A%2F%2Fyqlb </span></span></span><span class="line"><span class="cl"><span class="s">log.net%2Fsamples%2Fdata.html.cssselect.xml&#39;%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&amp;format=json&amp;diagnostics=true&amp;ca </span></span></span><span class="line"><span class="cl"><span class="s">llback=&#34;</span> </span></span><span class="line"><span class="cl"> http-get <span class="nb">nip </span>json&gt; { <span class="s">&#34;query&#34;</span> <span class="s">&#34;results&#34;</span> <span class="s">&#34;results&#34;</span> <span class="s">&#34;a&#34;</span> } </span></span><span class="line"><span class="cl"> [ <span class="nb">swap at </span>] <span class="nb">each </span>[ <span class="s">&#34;href&#34;</span> <span class="nb">swap at </span>] <span class="nb">map </span> </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;https://github.com&#34;</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">&#34;https://github.com/sinatra/sinatra&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;https://github.com/Sutto/barista&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;https://github.com/pypy/pypy&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;https://github.com/dysinger/apparatus&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;https://github.com/videlalvaro/Thumper&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;https://github.com/alunny/sleight&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;https://github.com/vimpr/vimperator-plugins&#34;</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">&#34;https://repopular.com&#34;</span> http-get <span class="nb">nip </span>parse-html </span></span><span class="line"><span class="cl"> [ [ name&gt;&gt; <span class="s">&#34;aside&#34;</span> <span class="nb">= </span>] <span class="nb">find drop </span>] </span></span><span class="line"><span class="cl"> [ [ name&gt;&gt; <span class="s">&#34;aside&#34;</span> <span class="nb">= </span>] <span class="nb">find-last drop </span>] </span></span><span class="line"><span class="cl"> [ <span class="nb">&lt;slice&gt; </span>] <span class="nb">tri </span></span></span><span class="line"><span class="cl"> [ name&gt;&gt; <span class="s">&#34;a&#34;</span> <span class="nb">= </span>] <span class="nb">filter </span></span></span><span class="line"><span class="cl"> [ attributes&gt;&gt; <span class="s">&#34;href&#34;</span> <span class="nb">swap at </span>] <span class="nb">map </span></span></span><span class="line"><span class="cl"> [ <span class="s">&#34;https://github.com&#34;</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">&#34;https://github.com/sinatra/sinatra&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;https://github.com/Sutto/barista&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;https://github.com/pypy/pypy&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;https://github.com/dysinger/apparatus&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;https://github.com/videlalvaro/Thumper&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;https://github.com/alunny/sleight&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;https://github.com/vimpr/vimperator-plugins&#34;</span> </span></span></code></pre></div> Longest Palindrome https://re.factorcode.org/2010/10/longest-palindrome.html Sun, 24 Oct 2010 14:04:00 -0700 https://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&#39;</span> <span class="nf">) </span>[ Letter? ] <span class="nb">filter </span>&gt;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 &ldquo;obvious&rdquo; (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&rsquo;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 &ldquo;empty&rdquo; 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"> :&gt; 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"> :&gt; 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">&#34;abc&#34;</span> [ <span class="m">. </span>] each-subseq </span></span><span class="line"><span class="cl"><span class="s">&#34;a&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;ab&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;abc&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;b&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;bc&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;c&#34;</span> </span></span></code></pre></div><p>Once we have that, it&rsquo;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>&#39;[ <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 -0700 https://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&rsquo;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">&#34;Factor!&#34;</span> </span></span><span class="line"><span class="cl"> { $url <span class="s">&#34;https://factorcode.org&#34;</span> } </span></span><span class="line"><span class="cl"> <span class="s">&#34;Development started in 2003&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Open source (BSD license)&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Influenced by Forth, Lisp, and Smalltalk&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Blurs the line between language and library&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Interactive development&#34;</span> </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> } <span class="s">&#34;Slides&#34;</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">&#34;Code!&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Try clicking on these:&#34;</span> </span></span><span class="line"><span class="cl"> { $code <span class="s">&#34;2 2 +&#34;</span> } </span></span><span class="line"><span class="cl"> { $vocab-link <span class="s">&#34;sequences&#34;</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">&#34;Slides&#34;</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 &ldquo;big features&rdquo; 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&rsquo;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 -0700 https://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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;%d 0 obj\n&#34;</span> sprintf <span class="s">&#34;\nendobj&#34;</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&#39;</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">&#34;\\b&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: \f </span> <span class="s">&#34;\\f&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: \n </span> <span class="s">&#34;\\n&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: \r </span> <span class="s">&#34;\\r&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: \t </span> <span class="s">&#34;\\t&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: \\ </span> <span class="s">&#34;\\\\&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: ( </span> <span class="s">&#34;\\(&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="sc">CHAR: ) </span> <span class="s">&#34;\\)&#34;</span> } </span></span><span class="line"><span class="cl"> } escape-string-by <span class="s">&#34;(&#34;</span> <span class="s">&#34;)&#34;</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>&lt;&lt;</code> and <code>&gt;&gt;</code>.</p> <p>Our PDF file will start with an &ldquo;info&rdquo; 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">&#34;&lt;&lt;&#34;</span> , </span></span><span class="line"><span class="cl"> <span class="s">&#34;/CreationDate D:&#34;</span> now <span class="s">&#34;%Y%m%d%H%M%S&#34;</span> strftime <span class="nb">append </span>, </span></span><span class="line"><span class="cl"> <span class="s">&#34;/Producer (Factor)&#34;</span> , </span></span><span class="line"><span class="cl"> <span class="s">&#34;/Author &#34;</span> <span class="s">&#34;USER&#34;</span> os-env <span class="s">&#34;unknown&#34;</span> <span class="nb">or </span>pdf-string <span class="nb">append </span>, </span></span><span class="line"><span class="cl"> <span class="s">&#34;/Creator (created with Factor)&#34;</span> , </span></span><span class="line"><span class="cl"> <span class="s">&#34;&gt;&gt;&#34;</span> , </span></span><span class="line"><span class="cl"> ] { } make <span class="s">&#34;\n&#34;</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">&#34;&lt;&lt;&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;/Type /Catalog&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;/Pages 4 0 R&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;&gt;&gt;&#34;</span> </span></span><span class="line"><span class="cl"> } <span class="s">&#34;\n&#34;</span> <span class="nb">join </span><span class="k">; </span></span></span></code></pre></div><p>As is typical of &ldquo;text2pdf&rdquo; 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">&#34;&lt;&lt;&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;/Type /Font&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;/Subtype /Type1&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;/BaseFont /Courier&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;&gt;&gt;&#34;</span> </span></span><span class="line"><span class="cl"> } <span class="s">&#34;\n&#34;</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 - &ldquo;612 by 792 points&rdquo; or &ldquo;8.5 by 11 inches&rdquo;) and a list of pages (or &ldquo;kids&rdquo;) 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">&#34;&lt;&lt;&#34;</span> , </span></span><span class="line"><span class="cl"> <span class="s">&#34;/Type /Pages&#34;</span> , </span></span><span class="line"><span class="cl"> <span class="s">&#34;/MediaBox [ 0 0 612 792 ]&#34;</span> , </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;/Count %d&#34;</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">&#34;%d 0 R &#34;</span> sprintf ] <span class="nb">map concat </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;/Kids [ &#34;</span> <span class="s">&#34;]&#34;</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">&#34;&gt;&gt;&#34;</span> , </span></span><span class="line"><span class="cl"> ] { } make <span class="s">&#34;\n&#34;</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">&#34;&lt;&lt;&#34;</span> , </span></span><span class="line"><span class="cl"> <span class="s">&#34;/Type /Page&#34;</span> , </span></span><span class="line"><span class="cl"> <span class="s">&#34;/Parent 4 0 R&#34;</span> , </span></span><span class="line"><span class="cl"> <span class="m">1 </span><span class="nb">+ </span><span class="s">&#34;/Contents %d 0 R&#34;</span> sprintf , </span></span><span class="line"><span class="cl"> <span class="s">&#34;/Resources &lt;&lt; /Font &lt;&lt; /F1 3 0 R &gt;&gt; &gt;&gt;&#34;</span> , </span></span><span class="line"><span class="cl"> <span class="s">&#34;&gt;&gt;&#34;</span> , </span></span><span class="line"><span class="cl"> ] { } make <span class="s">&#34;\n&#34;</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&#39;</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">&#34;&lt;&lt;\n/Length %d\n&gt;&gt;&#34;</span> sprintf ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;\nstream\n&#34;</span> <span class="s">&#34;\nendstream&#34;</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">&#34;BT&#34;</span> , </span></span><span class="line"><span class="cl"> <span class="s">&#34;54 738 Td&#34;</span> , </span></span><span class="line"><span class="cl"> <span class="s">&#34;/F1 10 Tf&#34;</span> , </span></span><span class="line"><span class="cl"> <span class="s">&#34;12 TL&#34;</span> , </span></span><span class="line"><span class="cl"> [ pdf-string <span class="s">&#34;&#39;&#34;</span> <span class="nb">append </span>, ] <span class="nb">each </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;ET&#34;</span> , </span></span><span class="line"><span class="cl"> ] { } make <span class="s">&#34;\n&#34;</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&gt;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">&#34;\t&#34;</span> split <span class="s">&#34; &#34;</span> <span class="nb">join </span>string-lines </span></span><span class="line"><span class="cl"> [ [ <span class="s">&#34; &#34;</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&gt;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>&lt;groups&gt; ] <span class="nb">map concat </span><span class="m">57 </span>&lt;groups&gt; <span class="k">; </span></span></span></code></pre></div><p>We can then take these &ldquo;pages&rdquo; 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&gt;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> &ldquo;end-of-file&rdquo; 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">&#34;xref&#34;</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">&#34;0 %d&#34;</span> sprintf , </span></span><span class="line"><span class="cl"> <span class="s">&#34;0000000000 65535 f&#34;</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">&#34;%010X 00000 n&#34;</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">&#34;trailer&#34;</span> , </span></span><span class="line"><span class="cl"> <span class="s">&#34;&lt;&lt;&#34;</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">&#34;/Size %d&#34;</span> sprintf , </span></span><span class="line"><span class="cl"> <span class="s">&#34;/Info 1 0 R&#34;</span> , </span></span><span class="line"><span class="cl"> <span class="s">&#34;/Root 2 0 R&#34;</span> , </span></span><span class="line"><span class="cl"> <span class="s">&#34;&gt;&gt;&#34;</span> , </span></span><span class="line"><span class="cl"> <span class="s">&#34;startxref&#34;</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">&#34;%d&#34;</span> sprintf , </span></span><span class="line"><span class="cl"> <span class="s">&#34;%%EOF&#34;</span> , </span></span><span class="line"><span class="cl"> ] { } make <span class="s">&#34;\n&#34;</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&gt;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">&#34;\n&#34;</span> <span class="nb">join </span><span class="s">&#34;\n&#34;</span> <span class="nb">append </span><span class="s">&#34;%PDF-1.4\n&#34;</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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> string&gt;lines lines&gt;pages pages&gt;objects objects&gt;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">&#34;.pdf&#34;</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 -0700 https://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">&ldquo;interview question&rdquo;</a> that was posted on StackOverflow awhile ago. It&rsquo;s not particularly complicated &ndash; basically asking &ldquo;given two strings, how to tell if one is the rotated version of the other?&rdquo;</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">&ldquo;short circuit&rdquo;</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 &ldquo;?&rdquo;) 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&amp;&amp; <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">&#34;stack&#34;</span> <span class="s">&#34;tacks&#34;</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">&#34;foo&#34;</span> <span class="s">&#34;bar&#34;</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 -0700 https://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&rsquo;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">&#34;/opt/local/bin/fortune&#34;</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&#39;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 &ldquo;fortune&rdquo; 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 &ldquo;cookie-jar format&rdquo;. 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">&#34;%\n&#34;</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">&#34;/usr/games/fortune/fortunes&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;/usr/share/games/fortune/fortunes&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;/usr/local/share/games/fortune/fortunes&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;/opt/local/share/games/fortune/fortunes&#34;</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">&gt;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">&#34;/opt/local/share/games/fortune/fortunes&#34;</span> ascii file-lines </span></span><span class="line"><span class="cl">{ <span class="s">&#34;%&#34;</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 -0700 https://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> (&ldquo;read-eval-print-loop&rdquo;) 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&rsquo;s</a> REPL (called the &ldquo;listener&rdquo;). 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&rsquo;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 -0700 https://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) &lt;= abs (numerator y')</code>, and</li> <li><code>denominator y &lt;= 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">=&gt;</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">a</span> <span class="ow">-&gt;</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">&lt;</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">&gt;</span> <span class="mi">0</span> <span class="ow">=</span> <span class="n">simplest&#39;</span> <span class="n">n</span> <span class="n">d</span> <span class="n">n&#39;</span> <span class="n">d&#39;</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="ow">=</span> <span class="o">-</span> <span class="n">simplest&#39;</span> <span class="p">(</span><span class="o">-</span><span class="n">n&#39;</span><span class="p">)</span> <span class="n">d&#39;</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&#39;</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&#39;</span> <span class="ow">=</span> <span class="n">numerator</span> <span class="n">nd&#39;</span> </span></span><span class="line"><span class="cl"> <span class="n">d&#39;</span> <span class="ow">=</span> <span class="n">denominator</span> <span class="n">nd&#39;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">simplest&#39;</span> <span class="n">n</span> <span class="n">d</span> <span class="n">n&#39;</span> <span class="n">d&#39;</span> <span class="c1">-- assumes 0 &lt; n%d &lt; n&#39;%d&#39;</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&#39;</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&#39;&#39;</span><span class="o">+</span><span class="n">d&#39;&#39;</span><span class="p">)</span> <span class="kt">:%</span> <span class="n">n&#39;&#39;</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&#39;</span><span class="p">,</span><span class="n">r&#39;</span><span class="p">)</span> <span class="ow">=</span> <span class="n">quotRem</span> <span class="n">n&#39;</span> <span class="n">d&#39;</span> </span></span><span class="line"><span class="cl"> <span class="n">nd&#39;&#39;</span> <span class="ow">=</span> <span class="n">simplest&#39;</span> <span class="n">d&#39;</span> <span class="n">r&#39;</span> <span class="n">d</span> <span class="n">r</span> </span></span><span class="line"><span class="cl"> <span class="n">n&#39;&#39;</span> <span class="ow">=</span> <span class="n">numerator</span> <span class="n">nd&#39;&#39;</span> </span></span><span class="line"><span class="cl"> <span class="n">d&#39;&#39;</span> <span class="ow">=</span> <span class="n">denominator</span> <span class="n">nd&#39;&#39;</span> </span></span></code></pre></div><p>I couldn&rsquo;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&#39;</span> <span class="nv">d&#39;</span> <span class="nf">-- </span><span class="nv">val</span> <span class="nf">) </span><span class="c">! assumes 0 &lt; n/d &lt; n&#39;/d&#39;</span> </span></span><span class="line"><span class="cl"> n d <span class="nb">/mod </span>:&gt; <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&#39; d&#39; <span class="nb">/mod </span>:&gt; <span class="nf">( </span><span class="nv">q&#39;</span> <span class="nv">r&#39;</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&#39; <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&#39; r&#39; d r (simplest) &gt;fraction :&gt; <span class="nf">( </span><span class="nv">n&#39;&#39;</span> <span class="nv">d&#39;&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> q n&#39;&#39; <span class="nb">* </span>d&#39;&#39; <span class="nb">+ </span>n&#39;&#39; <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">&gt; </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">&gt; </span>] [ x y [ &gt;fraction ] <span class="nb">bi@ </span>(simplest) ] } </span></span><span class="line"><span class="cl"> { [ y <span class="m">0 </span><span class="nb">&lt; </span>] [ y x [ <span class="nb">neg </span>&gt;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&gt;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&gt;ratio &#39;[ _ <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 &ldquo;grade school&rdquo; estimate <code>22/7</code>, and the second result is the &ldquo;best&rdquo; 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 -0700 https://re.factorcode.org/2010/09/internet-checksum.html <p>I needed to calculate the &ldquo;internet checksum&rdquo; for a small project I&rsquo;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&rsquo;t have support (until recently) for the &ldquo;internet checksum&rdquo;.</p> <p>The &ldquo;internet checksum&rdquo; 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&rsquo;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 &#34;count&#34; bytes </span></span></span><span class="line"><span class="cl"><span class="cm"> * beginning at location &#34;addr&#34;. </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">&gt;</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">&gt;</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">&gt;&gt;</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">&amp;</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">&gt;&gt;</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>&lt;sliced-groups&gt; [ le&gt; ] <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>&gt;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 -0700 https://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 &ldquo;1, 2, 3, and 4&rdquo; and the operators &ldquo;+, -, *, /, and ^&rdquo; (allowing the operators to be repeated, but not the numbers). It&rsquo;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&rsquo;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., &ldquo;<code>dup .</code>&rdquo;, 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&rsquo;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 &ldquo;1&rdquo; 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 &ldquo;divide-by-zero&rdquo; errors are produced, perhaps using <a href="https://docs.factorcode.org/content/word-assoc-filter,assocs.html">assoc-filter</a> to examine the &ldquo;bad&rdquo; 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&rsquo;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 &ldquo;swapped&rdquo; 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&rsquo;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&rsquo;t need &ldquo;swapped&rdquo; 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 &ldquo;dipped&rdquo; 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 -0700 https://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">&ldquo;What Every Computer Scientist Should Know About Floating-Point Arithmetic&rdquo;</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">&#34;cannot be special&#34;</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&gt;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&gt;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&gt;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&gt;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&gt;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&gt;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 -0700 https://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 &ldquo;Hello, world!&rdquo;. Our web application is structured into a dispatcher (our &ldquo;main responder&rdquo;), 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> &lt; <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">&lt;hello-action&gt;</span> <span class="nf">( -- </span><span class="nv">action</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> &lt;page-action&gt; </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Hello, world!&#34;</span> <span class="s">&#34;text/plain&#34;</span> &lt;content&gt; ] &gt;&gt;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">&lt;hello&gt;</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"> &lt;hello-action&gt; <span class="s">&#34;&#34;</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"> &lt;hello&gt; </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&rsquo;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">&lt;?xml version=&#39;1.0&#39; ?&gt;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nt">&lt;t:chloe</span> <span class="na">xmlns:t=</span><span class="s">&#34;https://factorcode.org/chloe/1.0&#34;</span><span class="nt">&gt;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;t:form</span> <span class="na">t:action=</span><span class="s">&#34;$hello&#34;</span><span class="nt">&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;label&gt;</span>What is your name?<span class="nt">&lt;/label&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;t:field</span> <span class="na">t:name=</span><span class="s">&#34;name&#34;</span> <span class="nt">/&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">&#34;submit&#34;</span> <span class="nt">/&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;/t:form&gt;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nt">&lt;/t:chloe&gt;</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 &ldquo;Hello, $name!&rdquo;:</p> <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">&lt;hello-action&gt;</span> <span class="nf">( -- </span><span class="nv">action</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> &lt;page-action&gt; </span></span><span class="line"><span class="cl"> { hello <span class="s">&#34;hello&#34;</span> } &gt;&gt;template </span></span><span class="line"><span class="cl"> [ </span></span><span class="line"><span class="cl"> <span class="s">&#34;name&#34;</span> param <span class="s">&#34;Hello, %s!&#34;</span> sprintf </span></span><span class="line"><span class="cl"> <span class="s">&#34;text/plain&#34;</span> &lt;content&gt; </span></span><span class="line"><span class="cl"> ] &gt;&gt;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">&lt;hello-action&gt;</span> <span class="nf">( -- </span><span class="nv">action</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> &lt;page-action&gt; </span></span><span class="line"><span class="cl"> [ </span></span><span class="line"><span class="cl"> { { <span class="s">&#34;name&#34;</span> [ v-required ] } } validate-params </span></span><span class="line"><span class="cl"> ] &gt;&gt;validate </span></span><span class="line"><span class="cl"> { hello <span class="s">&#34;hello&#34;</span> } &gt;&gt;template </span></span><span class="line"><span class="cl"> [ </span></span><span class="line"><span class="cl"> <span class="s">&#34;name&#34;</span> value <span class="s">&#34;Hello, %s!&#34;</span> sprintf </span></span><span class="line"><span class="cl"> <span class="s">&#34;text/plain&#34;</span> &lt;content&gt; </span></span><span class="line"><span class="cl"> ] &gt;&gt;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">&lt;alloy&gt;</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">&lt;hello&gt;</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"> &lt;hello-action&gt; <span class="s">&#34;&#34;</span> add-responder </span></span><span class="line"><span class="cl"> <span class="s">&#34;resource:hello.db&#34;</span> &lt;sqlite-db&gt; &lt;alloy&gt; <span class="k">; </span></span></span></code></pre></div><p>If you navigate to the website now, and don&rsquo;t provide a <code>name</code>, you&rsquo;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 &ldquo;counter&rdquo;, &ldquo;todo list&rdquo;, &ldquo;tiny url&rdquo;, and &ldquo;ip address&rdquo;) 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 -0700 https://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 &ldquo;code smell&rdquo; 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> =&gt; <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> &lt; <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">&lt;calculator&gt;</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">&#34;0&#34;</span> calculator new-model <span class="m">0 </span>&gt;&gt;x <span class="k">; </span></span></span></code></pre></div><p>If we want to reset the model (such as when we press the &ldquo;clear&rdquo; 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>&gt;&gt;x <span class="no">f </span>&gt;&gt;y <span class="no">f </span>&gt;&gt;op <span class="no">f </span>&gt;&gt;valid <span class="s">&#34;0&#34;</span> <span class="nb">swap </span>set-model <span class="k">; </span></span></span></code></pre></div><p>We&rsquo;re storing all values as floating-point numbers, but (for display purposes) we&rsquo;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">&gt;float </span>number&gt;string <span class="nb">dup </span><span class="s">&#34;.0&#34;</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&gt;&gt; string&gt;number &gt;&gt;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&gt;&gt; string&gt;number &gt;&gt;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"> &gt;&gt;op set-x <span class="no">f </span>&gt;&gt;y <span class="no">f </span>&gt;&gt;valid <span class="nb">drop </span><span class="k">; </span></span></span></code></pre></div><p>Pushing the &ldquo;=&rdquo; 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&gt;&gt; ] [ y&gt;&gt; ] [ op&gt;&gt; ] <span class="nb">tri </span>call( x y -- z ) </span></span><span class="line"><span class="cl"> [ &gt;&gt;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&gt;&gt; [ <span class="nb">dup </span>y&gt;&gt; [ 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&gt;&gt; [ </span></span><span class="line"><span class="cl"> <span class="nb">dup </span>value&gt;&gt; <span class="s">&#34;-&#34;</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">&#34;-&#34;</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 &ldquo;.&rdquo; 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&gt; </span></span><span class="line"><span class="cl"> [ [ <span class="nb">dup </span><span class="s">&#34;.&#34;</span> <span class="nb">subseq? </span>[ <span class="s">&#34;.&#34;</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>&gt;&gt;valid <span class="s">&#34;0.&#34;</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&gt; </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>&gt;&gt;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">&lt;calculator&gt; 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">&#34;C&#34;</span> calc <span class="nb">get-global </span>&#39;[ <span class="nb">drop </span>_ reset ] &lt;border-button&gt; <span class="k">; </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">&#34;±&#34;</span> calc <span class="nb">get-global </span>&#39;[ <span class="nb">drop </span>_ negate ] &lt;border-button&gt; <span class="k">; </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">&#34;+&#34;</span> calc <span class="nb">get-global </span>&#39;[ <span class="nb">drop </span>_ [ <span class="nb">+ </span>] set-op ] &lt;border-button&gt; <span class="k">; </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">&#34;-&#34;</span> calc <span class="nb">get-global </span>&#39;[ <span class="nb">drop </span>_ [ <span class="nb">- </span>] set-op ] &lt;border-button&gt; <span class="k">; </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">&#34;×&#34;</span> calc <span class="nb">get-global </span>&#39;[ <span class="nb">drop </span>_ [ <span class="nb">* </span>] set-op ] &lt;border-button&gt; <span class="k">; </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">&#34;÷&#34;</span> calc <span class="nb">get-global </span>&#39;[ <span class="nb">drop </span>_ [ <span class="nb">/ </span>] set-op ] &lt;border-button&gt; <span class="k">; </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">&#34;=&#34;</span> calc <span class="nb">get-global </span>&#39;[ <span class="nb">drop </span>_ solve ] &lt;border-button&gt; <span class="k">; </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">&#34;.&#34;</span> calc <span class="nb">get-global </span>&#39;[ <span class="nb">drop </span>_ decimal ] &lt;border-button&gt; <span class="k">; </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>&#39;[ <span class="nb">drop </span>_ _ digit ] &lt;border-button&gt; <span class="k">; </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">&#34;&#34;</span> &lt;label&gt; <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">&lt;display&gt;</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>&lt;label-control&gt; { <span class="m">5 5 </span>} &lt;border&gt; </span></span><span class="line"><span class="cl"> { <span class="m">1 </span>1/2 } &gt;&gt;align </span></span><span class="line"><span class="cl"> COLOR: gray &lt;solid&gt; &gt;&gt;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">&lt;col&gt;</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 &lt;track&gt; <span class="m">1 </span>&gt;&gt;fill { <span class="m">5 5 </span>} &gt;&gt;gap </span></span><span class="line"><span class="cl"> <span class="nb">swap </span>output&gt;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">&lt;row&gt;</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 &lt;track&gt; <span class="m">1 </span>&gt;&gt;fill { <span class="m">5 5 </span>} &gt;&gt;gap </span></span><span class="line"><span class="cl"> <span class="nb">swap </span>output&gt;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"> &lt;display&gt; </span></span><span class="line"><span class="cl"> [ [C] [±] [÷] [×] ] &lt;row&gt; </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;7&#34;</span> [#] <span class="s">&#34;8&#34;</span> [#] <span class="s">&#34;9&#34;</span> [#] [-] ] &lt;row&gt; </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;4&#34;</span> [#] <span class="s">&#34;5&#34;</span> [#] <span class="s">&#34;6&#34;</span> [#] [+] ] &lt;row&gt; </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;1&#34;</span> [#] <span class="s">&#34;2&#34;</span> [#] <span class="s">&#34;3&#34;</span> [#] [=] ] &lt;row&gt; </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;0&#34;</span> [#] [.] [_] [_] ] &lt;row&gt; </span></span><span class="line"><span class="cl"> ] &lt;col&gt; { <span class="m">10 10 </span>} &lt;border&gt; <span class="s">&#34;Calculator&#34;</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">&#34;calc-ui&#34;</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 -0700 https://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 &ndash; 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 (&rsquo;-&rsquo;) or absent, cat reads from the standard input.</em></p> </blockquote> <p>We&rsquo;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">&#34;cat&#34;</span> scaffold-work </span></span><span class="line"><span class="cl">Creating scaffolding for <span class="s">P&#34; resource:work/cat/cat.factor&#34;</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">&#34;cat&#34;</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">&gt;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">&#34;: not found&#34;</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 &ldquo;-&rdquo; (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">&#34;-&#34;</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">&#34;cat&#34;</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 &ldquo;Save&rdquo; to persist the deploy settings into a <code>deploy.factor</code> file, and &ldquo;Deploy&rdquo; 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 -0700 https://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 &ldquo;Marriage Sort&rdquo;. 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 &ldquo;conclusion&rdquo; 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 &ldquo;best so far&rdquo;.</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 &ldquo;best&rdquo; 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">- &gt;fixnum dup </span><span class="m">0 </span><span class="nb">&gt; </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&rsquo;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">&gt;= </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">&lt; </span>] [ </span></span><span class="line"><span class="cl"> <span class="nb">2dup </span>[ seq <span class="nb">nth </span>] <span class="nb">bi@ &lt; </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&#39;</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 &lt; </span>] [ </span></span><span class="line"><span class="cl"> <span class="nb">2over </span>[ seq <span class="nb">nth </span>] <span class="nb">bi@ &lt;= </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&rsquo;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 -0700 https://re.factorcode.org/2010/08/maybe-accessor.html <p><a href="https://factorcode.org">Factor</a> has support for standard &ldquo;object-oriented&rdquo; programming concepts such as classes and attributes. Recently, I wanted to &ldquo;get an attributes value (setting it first if not set)&rdquo;. I came up with a technique to do this, and wanted to share.</p> <p>First, some background. Defining a class &ldquo;person&rdquo; with attributes &ldquo;name&rdquo; and &ldquo;age&rdquo;:</p> <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">&#34;Frank&#34;</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">&#34;Frank&#34;</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">&#34;Frank&#34;</span> &gt;&gt;name <span class="m">20 </span>&gt;&gt;age <span class="m">. </span></span></span><span class="line"><span class="cl">T{ person { name <span class="s">&#34;Frank&#34;</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">&#34;Frank&#34;</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&gt; </span></span><span class="line"><span class="cl"><span class="s">&#34;Frank&#34;</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">&#34;Frank&#34;</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">&#34;Frank&#34;</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 &ldquo;get&rdquo; and &ldquo;set&rdquo; 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">&#34;Frank&#34;</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">&#34;Frank&#34;</span> } } </span></span></code></pre></div><p>Coming back to the original problem: how can I &ldquo;set an attribute if not set and then immediately get the attribute&rdquo;? Using the &ldquo;get, set, or change&rdquo; 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">&#34;Frank&#34;</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&gt;&gt; <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;Frank&#34;</span> </span></span></code></pre></div><p>One problem with that is it performs two get&rsquo;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&gt;&gt; [ <span class="nb">nip </span>] [ <span class="s">&#34;Frank&#34;</span> [ &gt;&gt;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">&#34;Frank&#34;</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"> [ [ &gt;&gt;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&gt;&gt; [ <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&gt;&gt;</code> or <code>|name&gt;&gt;</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">&#34;Joe&#34;</span> ] maybe-name <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;Joe&#34;</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">&#34;Frank&#34;</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">&#34;Joe&#34;</span> ] maybe-name <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;Frank&#34;</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&rsquo;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">&#34;maybe-&#34;</span> <span class="nb">prepend </span><span class="s">&#34;accessors&#34;</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>&gt;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">&#34;slots&#34;</span> word-prop [ </span></span><span class="line"><span class="cl"> <span class="nb">dup </span>read-only&gt;&gt; [ <span class="nb">drop </span>] [ name&gt;&gt; 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 &ldquo;maybe&rdquo; 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> &lt;&lt; person define-maybe-accessors <span class="nb">&gt; </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 -0700 https://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 &ldquo;practice, practice, practice&rdquo;. 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, &ldquo;listen&rdquo; and &ldquo;silent&rdquo; are anagrams of each other (i.e., when sorted, their letters are both &ldquo;eilnst&rdquo;).</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&rsquo;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"> &#39;[ <span class="nb">dup </span>natural-sort <span class="nb">&gt;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">&gt; </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">&#34;listen&#34;</span> <span class="s">&#34;silent&#34;</span> <span class="s">&#34;orange&#34;</span> } all-anagrams <span class="m">. </span></span></span><span class="line"><span class="cl">H{ { <span class="s">&#34;eilnst&#34;</span> V{ <span class="s">&#34;listen&#34;</span> <span class="s">&#34;silent&#34;</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">&#34;/usr/share/dict/words&#34;</span> ascii file-lines [ &gt;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"> &gt;lower natural-sort <span class="nb">&gt;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">&#34;listen&#34;</span> anagrams <span class="m">. </span></span></span><span class="line"><span class="cl">V{ <span class="s">&#34;enlist&#34;</span> <span class="s">&#34;listen&#34;</span> <span class="s">&#34;silent&#34;</span> <span class="s">&#34;tinsel&#34;</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">&#34;banana&#34;</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>&#39;[ <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">&#34;a&#34;</span> <span class="s">&#34;ab&#34;</span> <span class="s">&#34;abc&#34;</span> <span class="s">&#34;abcd&#34;</span> <span class="s">&#34;hjkl&#34;</span> } <span class="nb">longest </span><span class="m">. </span></span></span><span class="line"><span class="cl">{ <span class="s">&#34;abcd&#34;</span> <span class="s">&#34;hjkl&#34;</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>&#39;[ _ <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 &ldquo;groan&rdquo; is the most (10 words). And two anagrams are tied for longest: &ldquo;pneumohydropericardium/hydropneumopericardium&rdquo; and &ldquo;cholecystoduodenostomy/duodenocholecystostomy&rdquo;. Wouldn&rsquo;t you know, they&rsquo;d be medical words&hellip;</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 -0700 https://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&rsquo;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 &ldquo;fat arrows&rdquo; (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&rsquo;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 &ldquo;fat arrows&rdquo; to Factor.</p> <p>First, what is a &ldquo;fat arrow&rdquo;? It appears to be simple syntax to create two-element arrays, but without using &ldquo;array syntax&rdquo;. 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>=&gt; <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>=&gt; <span class="s">&#34;some text&#34;</span> <span class="m">. </span></span></span><span class="line"><span class="cl">{ <span class="no">t </span><span class="s">&#34;some text&#34;</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&rsquo;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>=&gt; </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&rsquo;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">&lt;=&gt;</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&rsquo;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 =&gt; [ ] <span class="c">! Do nothing</span> </span></span><span class="line"><span class="cl"> no =&gt; [ <span class="s">&#34;No way!&#34;</span> <span class="nb">throw </span>] </span></span><span class="line"><span class="cl"> maybe =&gt; [ <span class="s">&#34;Make up your mind!&#34;</span> <span class="nb">print </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Invalid input; try again.&#34;</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 -0700 https://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>&ldquo;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).&rdquo;</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 &ldquo;sum of the squares of its digits&rdquo;. We will be assuming all calculations are in base 10. One method is to use &ldquo;mod 10&rdquo; to calculate the last digit, then compute &ldquo;div 10&rdquo; and repeat until you&rsquo;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> (&ldquo;divmod&rdquo;) 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">&gt; </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 &ldquo;sum of the squares&rdquo; 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&rsquo;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>&lt;iota&gt; [ 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 &ldquo;happy primes&rdquo; 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>&lt;iota&gt; [ [ 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&rsquo;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>&ldquo;The palindromic prime 10<sup>150,006</sup> + 7426247 × 10<sup>75,000</sup> + 1 is also a happy prime with 150,007 digits&hellip;&rdquo;</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 -0700 https://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&rsquo;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">&gt; </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">&#34;/usr/share/dict/words&#34;</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">&#34;/bin/sh&#34;</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 -0700 https://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 &ldquo;ANCIENT SUMERIA&rdquo; 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 &lt;game&gt; <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">&#34;hamurabi&#34;</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 -0700 https://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">&gt; </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">&gt; </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> &ndash; 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">&gt; </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 -0700 https://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&rsquo;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">&#34;a really long string&#34;</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&gt;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&gt;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., &ldquo;[database]&rdquo;), 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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ <span class="s">&#34; \t\n\r&#34;</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&#39;</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&#39;</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 &ldquo;[section]&rdquo; or a &ldquo;name=value&rdquo; (but not both).</p> <p>We know a line is a section if it starts with &lsquo;[&rsquo; and ends with &lsquo;]&rsquo;:</p> <div class="highlight"><pre 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&#39;</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>&gt;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&#39;</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 &gt;hashtable <span class="k">; </span></span></span></code></pre></div><p>Implementing <code>write-ini</code> is pretty easy. It&rsquo;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">&#34;[%s]\n&#34;</span> printf ] <span class="nb">dip </span></span></span><span class="line"><span class="cl"> [ <span class="s">&#34;%s=%s\n&#34;</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&gt;ini</code> and <code>ini&gt;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&gt;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&gt;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., &ldquo;\t&rdquo;, &ldquo;\n&rdquo;, 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 &lsquo;#&rsquo; and &lsquo;;&rsquo; comment characters.</li> <li>Quoted strings (e.g., name=&ldquo;value&rdquo;).</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 -0700 https://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 &ldquo;upside-down&rdquo;. 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">&#34;abcdefghijklmnopqrstuvwxyz1234567890&#34;</span> flip-text <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;068Ɫ95ᔭƐᄅ⇂zʎxʍʌnʇsɹbdouɯʃʞɾᴉɥᵷɟǝpɔqɐ&#34;</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&rsquo;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&gt;flip</span> <span class="nf">( </span><span class="nv">ch</span> <span class="nf">-- </span><span class="nv">ch&#39;</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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ ch&gt;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 -0700 https://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 &ldquo;simple&rdquo; appears to mean both understandable and low token count &ndash; an attribute descriptively valued in Paul Graham&rsquo;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&rsquo;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">&amp;</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">&#34;odd degrees of freedom&#34;</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 &ldquo;guts&rdquo; of the <code>chi2P</code> word into an &ldquo;internal&rdquo; 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">&#34;odd degrees of freedom&#34;</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 &ldquo;better&rdquo;. 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 -0700 https://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&rsquo;s Number</a>, according to this observation:</p> <blockquote> <p><em>&ldquo;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&hellip;&rdquo;</em></p> </blockquote> <p>I wanted to contrast his solution with one using <a href="https://www.factorcode.org">Factor</a>. We&rsquo;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">&lt; </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&rsquo;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">&gt;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&rsquo;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 -0700 https://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 &ldquo;easier&rdquo; to remember or exchange than latitude/longitude pairs.</p> <p>The encoding, known as &ldquo;10:10&rdquo;, 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">&#34;ABCDEFGHJKMNPQRVWXY0123456789&#34;</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">&gt;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&gt;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 &gt;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&gt;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 -0700 https://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&rsquo;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> &ndash; specifying how many times (<code>4</code>) to roll a dice with a number (<code>8</code>) of sides.</p> <p>Let&rsquo;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])+ =&gt; [[ <span class="nb">&gt;string </span>string&gt;number ]] </span></span><span class="line"><span class="cl">dice <span class="nb">= </span><span class="s">&#34;d&#34;</span> <span class="nb">number </span> =&gt; [[ <span class="nb">second </span>&#39;[ _ random ] ]] </span></span><span class="line"><span class="cl">roll <span class="nb">= number </span>dice =&gt; [[ <span class="nb">first2 </span>&#39;[ <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>.* =&gt; [[ <span class="s">&#34;unknown dice&#34;</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">&#34;4d8&#34;</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">&#34;4d8&#34;</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">&#34;foo&#34;</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 &ldquo;base&rdquo; 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 -0700 https://re.factorcode.org/2010/05/evenly-partition-an-integer.html <p><em>Update: I just noticed after re-watching Slava&rsquo;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 &ldquo;distribute&rdquo;) 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">&lt;array&gt; </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 &ldquo;doing it wrong&rdquo; 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&rsquo;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">&lt;array&gt; </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">&lt;array&gt; </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">&lt;array&gt; </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">&lt;array&gt; </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">&lt;array&gt; </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">&lt;array&gt; </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&rsquo;t care what elements of the sequence are, only its length, so you may as well be using &ldquo;n&rdquo; instead of an array of &ldquo;n&rdquo; zeroes, so we can remove the <code>0 &lt;array&gt;</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 &ldquo;cleaned up&rdquo; 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&#39;</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&#39;</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 -0700 https://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 &ldquo;fake&rdquo; 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">&#34;Aaliyah&#34;</span> <span class="s">&#34;Aaron&#34;</span> <span class="s">&#34;Abagail&#34;</span> <span class="s">&#34;Abbey&#34;</span> <span class="s">&#34;Abbie&#34;</span> <span class="s">&#34;Abbigail&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Abby&#34;</span> <span class="s">&#34;Abdiel&#34;</span> <span class="s">&#34;Abdul&#34;</span> <span class="s">&#34;Abdullah&#34;</span> <span class="s">&#34;Abe&#34;</span> <span class="s">&#34;Abel&#34;</span> <span class="s">&#34;Abelardo&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Abigail&#34;</span> <span class="s">&#34;Abigale&#34;</span> <span class="s">&#34;Abigayle&#34;</span> <span class="s">&#34;Abner&#34;</span> <span class="s">&#34;Abraham&#34;</span> <span class="s">&#34;Ada&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Adah&#34;</span> <span class="s">&#34;Adalberto&#34;</span> <span class="s">&#34;Adaline&#34;</span> <span class="s">&#34;Adam&#34;</span> <span class="s">&#34;Adan&#34;</span> <span class="s">&#34;Addie&#34;</span> <span class="s">&#34;Addison&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Adela&#34;</span> <span class="s">&#34;Adelbert&#34;</span> <span class="s">&#34;Adele&#34;</span> <span class="s">&#34;Adelia&#34;</span> <span class="s">&#34;Adeline&#34;</span> <span class="s">&#34;Adell&#34;</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">&#34;Abbott&#34;</span> <span class="s">&#34;Abernathy&#34;</span> <span class="s">&#34;Abshire&#34;</span> <span class="s">&#34;Adams&#34;</span> <span class="s">&#34;Altenwerth&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Anderson&#34;</span> <span class="s">&#34;Ankunding&#34;</span> <span class="s">&#34;Armstrong&#34;</span> <span class="s">&#34;Auer&#34;</span> <span class="s">&#34;Aufderhar&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Bahringer&#34;</span> <span class="s">&#34;Bailey&#34;</span> <span class="s">&#34;Balistreri&#34;</span> <span class="s">&#34;Barrows&#34;</span> <span class="s">&#34;Bartell&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Bartoletti&#34;</span> <span class="s">&#34;Barton&#34;</span> <span class="s">&#34;Bashirian&#34;</span> <span class="s">&#34;Batz&#34;</span> <span class="s">&#34;Bauch&#34;</span> <span class="s">&#34;Baumbach&#34;</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">&#34; &#34;</span> <span class="nb">glue </span><span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;Greyson Barrows&#34;</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">&#34;###-###-####&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;(###)###-####&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;1-###-###-####&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;###.###.####&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;###-###-####&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;(###)###-####&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;1-###-###-####&#34;</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&#39;</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">&#34;0123456789&#34;</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">&#34;352-327-9815&#34;</span> </span></span></code></pre></div><p>For added flavor, the author chose to include &ldquo;business bullshit&rdquo; 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">&#34;leverage 24/7 models&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;deploy ubiquitous vortals&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;maximize holistic channels&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;exploit real-time niches&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;unleash proactive mindshare&#34;</span> </span></span></code></pre></div><p>And &ldquo;product catch phrase&rdquo; 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">&#34;Reverse-engineered value-added toolset&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;Diverse systemic concept&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;Ergonomic holistic pricing structure&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;Persevering local interface&#34;</span> </span></span><span class="line"><span class="cl"><span class="s">&#34;Intuitive human-resource time-frame&#34;</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 -0700 https://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">&lt; </span>[ <span class="s">&#34;negative seconds&#34;</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">&#34;s&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">60 </span><span class="s">&#34;m&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">24 </span><span class="s">&#34;h&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">7 </span><span class="s">&#34;d&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="m">52 </span><span class="s">&#34;w&#34;</span> } </span></span><span class="line"><span class="cl"> { <span class="no">f </span><span class="s">&#34;y&#34;</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">&gt; </span>[ number&gt;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">&#34;0s&#34;</span> ] [ <span class="nb">reverse </span><span class="s">&#34; &#34;</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">&#34;1d 10h 17m 36s&#34;</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 -0700 https://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 &ldquo;if/else if/else&rdquo; 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 &ldquo;human terms&rdquo;:</p> <div class="highlight"><pre 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">&lt; </span>[ <span class="s">&#34;negative seconds&#34;</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">&lt; </span>] [ <span class="nb">drop </span><span class="s">&#34;just now&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">60 </span><span class="nb">&lt; </span>] [ <span class="nb">drop </span><span class="s">&#34;less than a minute ago&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">120 </span><span class="nb">&lt; </span>] [ <span class="nb">drop </span><span class="s">&#34;about a minute ago&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">2700 </span><span class="nb">&lt; </span>] [ <span class="m">60 </span><span class="nb">/ </span><span class="s">&#34;%d minutes ago&#34;</span> sprintf ] } </span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">5400 </span><span class="nb">&lt; </span>] [ <span class="nb">drop </span><span class="s">&#34;about an hour ago&#34;</span> ] } </span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">86400 </span><span class="nb">&lt; </span>] [ <span class="m">3600 </span><span class="nb">/ </span><span class="s">&#34;%d hours ago&#34;</span> sprintf ] } </span></span><span class="line"><span class="cl"> { [ <span class="nb">dup </span><span class="m">172800 </span><span class="nb">&lt; </span>] [ <span class="nb">drop </span><span class="s">&#34;1 day ago&#34;</span> ] } </span></span><span class="line"><span class="cl"> [ <span class="m">86400 </span><span class="nb">/ </span><span class="s">&#34;%d days ago&#34;</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&rsquo;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 -0700 https://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 &lt;timestamp&gt; 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">&#34;CCCcIIIIIIIIIII&#34;</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 &ldquo;client&rdquo;):</p> <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">&lt;datagram&gt;</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">&lt;ntp&gt;</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>&lt;inet&gt; 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>&lt;inet4&gt; &lt;datagram&gt; [ </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">&#34;pool.ntp.org&#34;</span> &lt;ntp&gt; <span class="k">; </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">&#34;localhost&#34;</span> &lt;ntp&gt; <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 &ldquo;human-readable&rdquo; (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 -0700 https://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 &ldquo;epoch&rdquo;) 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">/ &gt;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">&#34;i&#34;</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>&lt;inet4&gt; &lt;datagram&gt; [ </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>&lt;inet4&gt; &lt;datagram&gt; [ </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 -0700 https://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&rsquo;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: &ldquo;what time is it?&rdquo;. 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">&#34;%c&#34;</span> strftime <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;Wed Apr 14 18:13:51 2010&#34;</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">&lt;daytime-server&gt;</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 &lt;threaded-server&gt; </span></span><span class="line"><span class="cl"> <span class="nb">swap </span>&gt;&gt;insecure </span></span><span class="line"><span class="cl"> <span class="s">&#34;daytime.server&#34;</span> &gt;&gt;name </span></span><span class="line"><span class="cl"> [ now <span class="s">&#34;%c&#34;</span> strftime <span class="nb">print flush </span>] &gt;&gt;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>&lt;daytime-server&gt; 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 -0700 https://re.factorcode.org/2010/04/connecting-to-memcached.html <p>The <a href="https://memcached.org/">Memcached</a> project provides a &ldquo;distributed memory object caching system&rdquo;, 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 &#39;^]&#39;. 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">&#34;127.0.0.1&#34;</span> <span class="m">11211 </span>&lt;inet&gt; 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">&lt;request&gt;</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">&#34;&#34;</span> <span class="s">&#34;&#34;</span> <span class="s">&#34;&#34;</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&gt;&gt; ] </span></span><span class="line"><span class="cl"> [ key&gt;&gt; <span class="nb">length </span>] </span></span><span class="line"><span class="cl"> [ extra&gt;&gt; <span class="nb">length </span>] </span></span><span class="line"><span class="cl"> [ </span></span><span class="line"><span class="cl"> [ key&gt;&gt; <span class="nb">length </span>] </span></span><span class="line"><span class="cl"> [ extra&gt;&gt; <span class="nb">length </span>] </span></span><span class="line"><span class="cl"> [ val&gt;&gt; <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&gt;&gt; ] </span></span><span class="line"><span class="cl"> [ cas&gt;&gt; ] </span></span><span class="line"><span class="cl"> } <span class="nb">cleave </span></span></span><span class="line"><span class="cl"> &#39;[ <span class="m">0x80 </span>_ _ _ <span class="m">0 0 </span>_ _ _ ] <span class="s">&#34;CCSCCSIIQ&#34;</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"> [ &gt;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&gt;&gt; (send) ] </span></span><span class="line"><span class="cl"> [ key&gt;&gt; (send) ] </span></span><span class="line"><span class="cl"> [ val&gt;&gt; (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">&#34;CCSCCSIIQ&#34;</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">&#34;bad magic&#34;</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">&#34;key not found&#34;</span> <span class="nb">throw </span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x02 </span>[ <span class="s">&#34;key exists&#34;</span> <span class="nb">throw </span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x03 </span>[ <span class="s">&#34;value too large&#34;</span> <span class="nb">throw </span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x04 </span>[ <span class="s">&#34;invalid arguments&#34;</span> <span class="nb">throw </span>] } </span></span><span class="line"><span class="cl"> { <span class="m">0x05 </span>[ <span class="s">&#34;item not stored&#34;</span> <span class="nb">throw </span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x06 </span>[ <span class="s">&#34;value not numeric&#34;</span> <span class="nb">throw </span>] } </span></span><span class="line"><span class="cl"> { <span class="m">0x81 </span>[ <span class="s">&#34;unknown command&#34;</span> <span class="nb">throw </span> ] } </span></span><span class="line"><span class="cl"> { <span class="m">0x82 </span>[ <span class="s">&#34;out of memory&#34;</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">&gt; </span>[ <span class="nb">read &gt;string </span>] [ <span class="nb">drop </span><span class="s">&#34;&#34;</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>&lt;request&gt; <span class="nb">swap </span>&gt;&gt;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>&lt;request&gt; <span class="nb">swap </span>&gt;&gt;key <span class="nb">swap </span>&gt;&gt;val </span></span><span class="line"><span class="cl"> { <span class="m">0 0 </span>} <span class="s">&#34;II&#34;</span> pack-be &gt;&gt;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">&#34;bar&#34;</span> <span class="s">&#34;foo&#34;</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">&#34;foo&#34;</span> m/get ] with-memcached <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;bar&#34;</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 -0700 https://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 &ldquo;close&rdquo; 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 &ldquo;fixes&rdquo; (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&rsquo;t be Peter Norvig if it didn&rsquo;t have probabilities in it. And sure enough, he introduces a very simple solution for the &ldquo;most likely correct word&rdquo;, 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"> &gt;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>[ &#39;[ _ <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">&#34;vocab:spelling/big.txt&#34;</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&rsquo;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">&#39;abcdefghijklmnopqrstuvwxyz&#39;</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">&gt;</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">&#34;abcdefghijklmnopqrstuvwxyz&#34;</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&#39;</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&#39;</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">&gt; </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">&#34;&#34;</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">&#34;&#34;</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&#39;</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">&gt; </span>] <span class="nb">filter </span>[ </span></span><span class="line"><span class="cl"> <span class="nb">first2 </span>&#39;[ <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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> [ </span></span><span class="line"><span class="cl"> &#39;[ <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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> NWORDS &#39;[ _ <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>&gt;=&lt; ] 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&#39;/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">&gt; </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">&#34;speling&#34;</span> correct <span class="m">. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;spelling&#34;</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 -0800 https://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 &ldquo;template language&rdquo;), 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">&#34;Content-type: text/html\n\n&#34;</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">&#34;&#34;&#34; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;% USING: calendar formatting math math.parser io ; %&gt; </span></span></span><span class="line"><span class="cl"><span class="s"> </span></span></span><span class="line"><span class="cl"><span class="s">&lt;html&gt; </span></span></span><span class="line"><span class="cl"><span class="s"> &lt;head&gt;&lt;title&gt;Simple Embedded Factor Example&lt;/title&gt;&lt;/head&gt; </span></span></span><span class="line"><span class="cl"><span class="s"> &lt;body&gt; </span></span></span><span class="line"><span class="cl"><span class="s"> The time is &lt;% now &#34;%c&#34; strftime write %&gt; </span></span></span><span class="line"><span class="cl"><span class="s"> &lt;br&gt; </span></span></span><span class="line"><span class="cl"><span class="s"> &lt;% 5 [ %&gt;&lt;p&gt;I like repetition&lt;/p&gt;&lt;% ] times %&gt; </span></span></span><span class="line"><span class="cl"><span class="s"> &lt;/body&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;/html&gt; </span></span></span><span class="line"><span class="cl"><span class="s"> </span></span></span><span class="line"><span class="cl"><span class="s">&#34;&#34;&#34;</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 -0800 https://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 &ldquo;URL-encoded&rdquo; (as described in <a href="https://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>). This is similar to how certain characters are &ldquo;escaped&rdquo; 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 &ldquo;content types&rdquo;. 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">&#34;;&#34;</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 &ldquo;application/x-www-form-urlencoded&rdquo;. 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">&#34;CONTENT_LENGTH&#34;</span> os-env <span class="s">&#34;0&#34;</span> <span class="nb">or </span>string&gt;number </span></span><span class="line"><span class="cl"> <span class="nb">read </span>[ <span class="s">&#34;&#34;</span> ] [ <span class="s">&#34;&amp;&#34;</span> <span class="nb">append </span>] <span class="nb">if-empty </span></span></span><span class="line"><span class="cl"> <span class="s">&#34;QUERY_STRING&#34;</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&rsquo;s worth noting that some forms can be submitted with a mime-type of &ldquo;multipart/form-data&rdquo;, 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">&#34;multipart unsupported&#34;</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">&#34;CONTENT_TYPE&#34;</span> os-env <span class="s">&#34;&#34;</span> <span class="nb">or </span>(content-type) { </span></span><span class="line"><span class="cl"> { <span class="s">&#34;multipart/form-data&#34;</span> [ (multipart) ] } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;application/x-www-form-urlencoded&#34;</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>&lt;cgi-form&gt;</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">&lt;cgi-form&gt;</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">&#34;REQUEST_METHOD&#34;</span> os-env <span class="s">&#34;GET&#34;</span> <span class="nb">or </span>&gt;upper { </span></span><span class="line"><span class="cl"> { <span class="s">&#34;GET&#34;</span> [ parse-get ] } </span></span><span class="line"><span class="cl"> { <span class="s">&#34;POST&#34;</span> [ parse-post ] } </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Unknown request method&#34;</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 &ldquo;get&rdquo; to &ldquo;post&rdquo;, 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 -0800 https://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 &ldquo;form handlers&rdquo;. 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 &ldquo;get&rdquo; the contents of the URL and send it back. When HTML forms are submitted using the <code>GET</code> method, the form elements are &ldquo;URL encoded&rdquo; and passed to the server as the &ldquo;query string&rdquo; part of the URL.</p> <p>For example, if I had a &ldquo;calculator&rdquo; application to add two numbers (e.g., &ldquo;x+y&rdquo;), you could imagine getting the result of <code>2+3</code> by calling:</p> <pre tabindex="0"><code>https://server/add?x=2&amp;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">&#34;x=2&amp;y=3&#34;</span> query&gt;assoc <span class="m">. </span></span></span><span class="line"><span class="cl">H{ { <span class="s">&#34;x&#34;</span> <span class="s">&#34;2&#34;</span> } { <span class="s">&#34;y&#34;</span> <span class="s">&#34;3&#34;</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&gt;assoc</a> word isn&rsquo;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">&#34;&#34;</span> query&gt;assoc <span class="m">. </span></span></span><span class="line"><span class="cl">H{ { <span class="s">&#34;&#34;</span> <span class="no">f </span>} } </span></span></code></pre></div><p>Also, it doesn&rsquo;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">&#34;a=2&amp;a=3&#34;</span> query&gt;assoc <span class="m">. </span></span></span><span class="line"><span class="cl">H{ { <span class="s">&#34;a&#34;</span> { <span class="s">&#34;2&#34;</span> <span class="s">&#34;3&#34;</span> } } } </span></span></code></pre></div><p>So to &ldquo;fix&rdquo; 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&gt;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&rsquo;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">&#34;QUERY_STRING&#34;</span> os-env <span class="s">&#34;&#34;</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">&lt;cgi-form&gt;</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">&#34;REQUEST_METHOD&#34;</span> os-env <span class="s">&#34;GET&#34;</span> <span class="nb">or </span>&gt;upper { </span></span><span class="line"><span class="cl"> { <span class="s">&#34;GET&#34;</span> [ parse-get ] } </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Unknown request method&#34;</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">&lt;cgi-simple-form&gt;</span> <span class="nf">( -- </span><span class="nv">assoc</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> &lt;cgi-form&gt; [ <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">&#34;Content-type: text/html\n\n&#34;</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">&#34;code&#34;</span> &lt;cgi-simple-form&gt; <span class="nb">at </span></span></span><span class="line"><span class="cl"><span class="s">&#34;&#34;&#34; </span></span></span><span class="line"><span class="cl"><span class="s">++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-] </span></span></span><span class="line"><span class="cl"><span class="s">&gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++.&gt;.+++. </span></span></span><span class="line"><span class="cl"><span class="s">------.--------.&gt;+.&gt;. </span></span></span><span class="line"><span class="cl"><span class="s">&#34;&#34;&#34;</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">&#34;&#34;&#34; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;html&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;head&gt;&lt;title&gt;Brainfuck&lt;/title&gt;&lt;/head&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;body&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;form method=&#39;get&#39;&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;textarea id=&#34;text&#34; name=&#34;code&#34; cols=&#34;80&#34; rows=&#34;15&#34;&gt; </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">&lt;/textarea&gt;&lt;br&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;input type=&#34;submit&#34; value=&#34;Submit&#34;&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;input type=&#34;reset&#34; value=&#34;Reset&#34;&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;/form&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;pre&gt;%s&lt;/pre&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;/body&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;/html&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&#34;&#34;&#34;</span> printf </span></span></code></pre></div><p>If nothing is specified, this will happily calculate and then print &ldquo;Hello World!&rdquo;, 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 -0800 https://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&rsquo;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 &ldquo;Common Gateway Interface&rdquo; (sometimes called &ldquo;CGI/1.1&rdquo;) is the documented version of conventions developed for web programming in the late 1990&rsquo;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">&#34;Content-type: text/html\n\n&#34;</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">&#34;&#34;&#34; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;html&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;head&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;title&gt;Debug&lt;/title&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;/head&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;body&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;pre&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&#34;&#34;&#34;</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">&gt;alist </span>sort-keys [ </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;&lt;b&gt;&#34;</span> <span class="nb">write first write </span><span class="s">&#34;&lt;/b&gt;&#34;</span> <span class="nb">write </span>] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34; = &#34;</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">&#34;&#34;&#34; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;/pre&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;/body&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&lt;/html&gt; </span></span></span><span class="line"><span class="cl"><span class="s">&#34;&#34;&#34;</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&rsquo;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&hellip;</p> Counting Word Frequencies https://re.factorcode.org/2009/12/counting-word-frequencies.html Wed, 30 Dec 2009 11:50:00 -0800 https://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 &ldquo;<code>\w</code>&rdquo; 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&#39;</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"> &#39;[ </span></span><span class="line"><span class="cl"> ascii file-contents &gt;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">&#34;%s\t%d\n&#34;</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">&gt;alist </span>[ </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;/tmp/counts-decreasing-factor&#34;</span> ascii ] <span class="nb">dip </span></span></span><span class="line"><span class="cl"> &#39;[ _ 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">&#34;/tmp/counts-alphabetical-factor&#34;</span> ascii ] <span class="nb">dip </span></span></span><span class="line"><span class="cl"> &#39;[ _ 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&rsquo;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&rsquo;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">&#39;/tmp/20_newsgroups&#39;</span> </span></span><span class="line"><span class="cl"><span class="c1">#root = &#39;/tmp/mini_newsgroups&#39;</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">&#39;\w+&#39;</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">&#34;Writing counts in decreasing order&#34;</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">&#39;counts-decreasing-python&#39;</span><span class="p">,</span> <span class="s1">&#39;w&#39;</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">&gt;&gt;</span> <span class="n">f</span><span class="p">,</span> <span class="s1">&#39;</span><span class="si">%s</span><span class="se">\t</span><span class="si">%d</span><span class="s1">&#39;</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">&#34;Writing counts in decreasing order&#34;</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">&#39;counts-alphabetical-python&#39;</span><span class="p">,</span> <span class="s1">&#39;w&#39;</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">&gt;&gt;</span> <span class="n">f</span><span class="p">,</span> <span class="s1">&#39;</span><span class="si">%s</span><span class="se">\t</span><span class="si">%d</span><span class="s1">&#39;</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">&#39;Finished in </span><span class="si">%s</span><span class="s1"> seconds&#39;</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 -0800 https://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&rsquo;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&rsquo;t exist, it can be built relatively easily.</p> <p>I&rsquo;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 &ldquo;setup&rdquo; functionality of the word, from the &ldquo;work&rdquo; functionality.</p> <p>Let&rsquo;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&gt;&gt; { </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"> &#39;[ 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">&#34;unsupported&#34;</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">&#34;/tmp/foo&#34;</span> list-files <span class="m">. </span></span></span><span class="line"><span class="cl">V{ <span class="s">&#34;/tmp/foo/bar&#34;</span> <span class="s">&#34;/tmp/foo/baz/foo&#34;</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 -0800 https://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">&#34;\r\n\t.,\&#34; &#34;</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&#39;</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&#39;</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 &ldquo;feel&rdquo; 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">&#34; &#34;</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">&#34;the&#34;</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">&#34;/tmp/345.txt&#34;</span> ascii [ <span class="m">250 </span>wordgen ] with-file-reader <span class="m">. </span></span></span></code></pre></div><blockquote> <p><em>&ldquo;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&rsquo;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&rdquo;</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 -0700 https://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&rsquo;d walk through the creation of a small macro for parsing &ldquo;calculator&rdquo; expressions, as a small demonstration of their utility.</p> <p>First, the goal. We&rsquo;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">&#34;2+2&#34;</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">&#34;-&#34;</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">&#34;.&#34;</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">&#34;-&#34;</span> </span></span><span class="line"><span class="cl">=&gt; [[ <span class="nb">&gt;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">=&gt; [[ <span class="nb">&gt;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">&#34;.&#34;</span> ([0-9])* </span></span><span class="line"><span class="cl">=&gt; [[ [ <span class="nb">&gt;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">=&gt; [[ [ <span class="no">f </span><span class="nb">eq? not </span>] <span class="nb">filter concat </span>string&gt;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 &ldquo;+&rdquo;, &ldquo;-&rdquo;, &ldquo;*&rdquo;, and &ldquo;/&rdquo; 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">&#34;+&#34;</span> =&gt; [[ [ <span class="nb">+ </span>] ]] </span></span><span class="line"><span class="cl">sub <span class="nb">= </span><span class="s">&#34;-&#34;</span> =&gt; [[ [ <span class="nb">- </span>] ]] </span></span><span class="line"><span class="cl">mul <span class="nb">= </span><span class="s">&#34;*&#34;</span> =&gt; [[ [ <span class="nb">* </span>] ]] </span></span><span class="line"><span class="cl">div <span class="nb">= </span><span class="s">&#34;/&#34;</span> =&gt; [[ [ <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">=&gt; [[ [ <span class="nb">first </span>] [ <span class="nb">third </span>] [ <span class="nb">second </span>] <span class="nb">tri </span>&#39;[ _ _ @ ] ]] </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">&#34;-&#34;</span> =&gt; [[ <span class="nb">&gt;string </span>]] </span></span><span class="line"><span class="cl">whole <span class="nb">= </span>([0-9])* =&gt; [[ <span class="nb">&gt;string </span>]] </span></span><span class="line"><span class="cl">digit <span class="nb">= </span><span class="s">&#34;.&#34;</span> ([0-9])* =&gt; [[ [ <span class="nb">&gt;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">=&gt; [[ [ <span class="no">f </span><span class="nb">eq? not </span>] <span class="nb">filter concat </span>string&gt;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">&#34;+&#34;</span> =&gt; [[ [ <span class="nb">+ </span>] ]] </span></span><span class="line"><span class="cl">sub <span class="nb">= </span><span class="s">&#34;-&#34;</span> =&gt; [[ [ <span class="nb">- </span>] ]] </span></span><span class="line"><span class="cl">mul <span class="nb">= </span><span class="s">&#34;*&#34;</span> =&gt; [[ [ <span class="nb">* </span>] ]] </span></span><span class="line"><span class="cl">div <span class="nb">= </span><span class="s">&#34;/&#34;</span> =&gt; [[ [ <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">=&gt; [[ [ <span class="nb">first </span>] [ <span class="nb">third </span>] [ <span class="nb">second </span>] <span class="nb">tri </span>&#39;[ _ _ @ ] ]] </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">&#34;2+2&#34;</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 &ldquo;mod&rdquo;, &ldquo;shift&rdquo;, &ldquo;pow&rdquo;, etc.), functions (such as &ldquo;sin&rdquo;, &ldquo;sqrt&rdquo;, etc.), spaces in the expressions, chained expressions, grouped expressions, input validation, compile-time calculations, and other useful features. But, then it wouldn&rsquo;t be so simple&hellip;</p> IPInfoDB https://re.factorcode.org/2009/06/ipinfodb.html Fri, 26 Jun 2009 18:27:00 -0700 https://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&rsquo;t been many convenient ways to do this, but a recent website called <a href="https://www.ipinfodb.com">IPInfoDB</a> (previously known as &ldquo;IP Location Tools&rdquo;), 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&gt;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&gt;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">&#34;Ip&#34;</span> find-tag &gt;&gt;ip ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;CountryCode&#34;</span> find-tag &gt;&gt;country-code ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;CountryName&#34;</span> find-tag &gt;&gt;country-name ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;RegionCode&#34;</span> find-tag &gt;&gt;region-code ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;RegionName&#34;</span> find-tag &gt;&gt;region-name ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;City&#34;</span> find-tag &gt;&gt;city ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;ZipPostalCode&#34;</span> find-tag &gt;&gt;zip-code ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Latitude&#34;</span> find-tag &gt;&gt;latitude ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Longitude&#34;</span> find-tag &gt;&gt;longitude ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Gmtoffset&#34;</span> find-tag &gt;&gt;gmtoffset ] </span></span><span class="line"><span class="cl"> [ <span class="s">&#34;Dstoffset&#34;</span> find-tag &gt;&gt;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&rsquo;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">&#34;https://ipinfodb.com/ip_query.php&#34;</span> </span></span><span class="line"><span class="cl"> http-get string&gt;xml xml&gt;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">&#34;https://ipinfodb.com/ip_query.php?ip=&#34;</span> <span class="nb">prepend </span></span></span><span class="line"><span class="cl"> http-get string&gt;xml xml&gt;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">&#34;74.125.45.100&#34;</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">&#34;74.125.45.100&#34;</span> } </span></span><span class="line"><span class="cl"> { country-code <span class="s">&#34;US&#34;</span> } </span></span><span class="line"><span class="cl"> { country-name <span class="s">&#34;United States&#34;</span> } </span></span><span class="line"><span class="cl"> { region-code <span class="s">&#34;06&#34;</span> } </span></span><span class="line"><span class="cl"> { region-name <span class="s">&#34;California&#34;</span> } </span></span><span class="line"><span class="cl"> { city <span class="s">&#34;Mountain View&#34;</span> } </span></span><span class="line"><span class="cl"> { zip-code <span class="s">&#34;94043&#34;</span> } </span></span><span class="line"><span class="cl"> { latitude <span class="s">&#34;37.4192&#34;</span> } </span></span><span class="line"><span class="cl"> { longitude <span class="s">&#34;-122.057&#34;</span> } </span></span><span class="line"><span class="cl"> { gmtoffset <span class="s">&#34;-8.0&#34;</span> } </span></span><span class="line"><span class="cl"> { dstoffset <span class="s">&#34;-7.0&#34;</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 -0700 https://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 &ldquo;Hello, world!&rdquo;. In Brainfuck, this could be written as:</p> <pre tabindex="0"><code>++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-] &gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++ .&gt;.+++.------.--------.&gt;+.&gt;. </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">&#34; </span></span></span><span class="line"><span class="cl"><span class="s"> ++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-] </span></span></span><span class="line"><span class="cl"><span class="s"> &gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++ </span></span></span><span class="line"><span class="cl"><span class="s"> .&gt;.+++.------.--------.&gt;+.&gt;. </span></span></span><span class="line"><span class="cl"><span class="s"> &#34;</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 &ldquo;Hello, world!&rdquo; example becomes:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-factor" data-lang="factor"><span class="line"><span class="cl">&lt;brainfuck&gt; </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>(&gt;) <span class="m">7 </span>(+) <span class="m">1 </span>(&gt;) <span class="m">10 </span>(+) <span class="m">1 </span>(&gt;) <span class="m">3 </span>(+) </span></span><span class="line"><span class="cl"> <span class="m">1 </span>(&gt;) <span class="m">1 </span>(+) <span class="m">4 </span>(&lt;) <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>(&gt;) <span class="m">2 </span>(+) (.) <span class="m">1 </span>(&gt;) <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>(&gt;) <span class="m">2 </span>(+) (.) </span></span><span class="line"><span class="cl"><span class="m">2 </span>(&lt;) <span class="m">15 </span>(+) (.) <span class="m">1 </span>(&gt;) (.) <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>(&gt;) <span class="m">1 </span>(+) (.) </span></span><span class="line"><span class="cl"><span class="m">1 </span>(&gt;) (.) <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 -0800 https://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">&#34;USING: io io.pathnames vocabs vocabs.loader ; &#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">printf</span> <span class="s2">&#34;\&#34;%s\&#34; &lt;vocab&gt; vocab-source-path (normalize-path) print&#34;</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> &gt; <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">&#34;</span><span class="nv">$dn</span><span class="s2">&#34;</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">&#34;Warning: directory &#39;</span><span class="nv">$1</span><span class="s2">&#39; not found&#34;</span> 1&gt;<span class="p">&amp;</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 -0800 https://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&rsquo;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 -0800 https://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&rsquo;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 -0800 https://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 &amp; Combinators</a></li> </ul> wp.factor https://re.factorcode.org/2008/12/wp-factor.html Tue, 09 Dec 2008 11:01:00 -0800 https://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&#39;</span> <span class="nf">) </span></span></span><span class="line"><span class="cl"> <span class="s">&#34; &#34;</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">&gt;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&gt;string <span class="s">&#34; &#34;</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 -0700 https://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 &ldquo;min&rdquo; word operates on &ldquo;value&rdquo; and &ldquo;b&rdquo;, placing the &ldquo;result&rdquo; on the stack, then the &ldquo;max&rdquo; word operates on &ldquo;a&rdquo; and &ldquo;result&rdquo;, 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 -0700 https://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>