<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.3.2">Jekyll</generator><link href="https://neckdeep.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://neckdeep.dev/" rel="alternate" type="text/html" hreflang="en" /><updated>2025-09-16T12:11:06+00:00</updated><id>https://neckdeep.dev/feed.xml</id><title type="html">Neck-Deep Development</title><subtitle>Neck-Deep Development is the blog and portfolio website for Danny Fekete, a teacher, programmer, game designer, and pretentious list-writer.
</subtitle><author><name>Danny Fekete</name><email>danny@neckdeep.dev</email></author><entry><title type="html">Treasure: Michael Runtz’ Wild by Nature</title><link href="https://neckdeep.dev/blog/2025-09-15-michael-runtzs-wild-by-nature/" rel="alternate" type="text/html" title="Treasure: Michael Runtz’ Wild by Nature" /><published>2025-09-15T00:00:00+00:00</published><updated>2025-09-16T12:10:24+00:00</updated><id>https://neckdeep.dev/blog/michael-runtzs-wild-by-nature</id><content type="html" xml:base="https://neckdeep.dev/blog/2025-09-15-michael-runtzs-wild-by-nature/"><![CDATA[<p><strong>Image Credits:</strong> The title image for this post was captured from Episode Six of the series, “Life in High Places”—one of the episodes that’s been floating around online the longest and fed my obsession with finding the rest.</p>

<blockquote class="note smaller" title="TLDR">
  <p>Carolyn reminds me that modern humans use YouTube <em>playlists</em> rather than badly SEO’d blog posts to find their curated content packages these days, so you’ll probably want to go directly <a href="https://www.youtube.com/playlist?list=PL-XR6mOWbLP76xopGlo1UJ2wDwXlbIZ2Q">here</a>.  Or, at least, <a href="#programme-listing">skip past my blather</a> and get to the videos.</p>

  <p>… But in case you’re <em>also</em> the obsolete type, hello.  Tea?</p>
</blockquote>

<ul id="markdown-toc">
  <li><a href="#context" id="markdown-toc-context">Context</a></li>
  <li><a href="#programme-listing" id="markdown-toc-programme-listing">Programme Listing</a>    <ul>
      <li><a href="#episode-01-the-rites-of-spring" id="markdown-toc-episode-01-the-rites-of-spring">Episode 01: The Rites of Spring</a></li>
      <li><a href="#episode-02-world-in-a-marsh" id="markdown-toc-episode-02-world-in-a-marsh">Episode 02: World in a Marsh</a></li>
      <li><a href="#episode-03-due-south" id="markdown-toc-episode-03-due-south">Episode 03: Due South</a></li>
      <li><a href="#episode-04-oasis-of-life" id="markdown-toc-episode-04-oasis-of-life">Episode 04: Oasis of Life</a></li>
      <li><a href="#episode-05-beauty-and-the-beasts" id="markdown-toc-episode-05-beauty-and-the-beasts">Episode 05: Beauty and the Beasts</a></li>
      <li><a href="#episode-06-life-in-high-places" id="markdown-toc-episode-06-life-in-high-places">Episode 06: Life in High Places</a></li>
      <li><a href="#episode-07-worlds-afloat" id="markdown-toc-episode-07-worlds-afloat">Episode 07: Worlds Afloat</a></li>
      <li><a href="#episode-08-the-toothed-ones" id="markdown-toc-episode-08-the-toothed-ones">Episode 08: The Toothed Ones</a></li>
      <li><a href="#episode-09-the-tides-of-life" id="markdown-toc-episode-09-the-tides-of-life">Episode 09: The Tides of Life</a></li>
      <li><a href="#episode-10-webs-of-intrigue" id="markdown-toc-episode-10-webs-of-intrigue">Episode 10: Webs of Intrigue</a></li>
      <li><a href="#episode-11-the-howls-of-august" id="markdown-toc-episode-11-the-howls-of-august">Episode 11: The Howls of August</a></li>
      <li><a href="#episode-12-the-journey" id="markdown-toc-episode-12-the-journey">Episode 12: The Journey</a></li>
      <li><a href="#episode-13-the-rut" id="markdown-toc-episode-13-the-rut">Episode 13: The Rut</a></li>
    </ul>
  </li>
</ul>

<h2 id="context">Context</h2>

<p>I heard about Michael Runtz through friends at Carleton University in 2009 when I was already at OISE working on my M.A., and more and more I think if I’d encountered him as an undergrad, today I’d probably be typing this on a wood-panelled Corona, vaguely probing the last of the season’s righteously-endured <a href="https://www.algonquinpark.on.ca/visit/park_management/mosquitoes-and-blackflies-(biting-insects).php">deer and horse fly bites</a>, and contemplating my fuel and rations stockpile for overwintering in a lonely and isolated observation post.  —But, you know, in a <em>good</em> way.</p>

<p>I’ll gush about Runtz at greater length soon, but what you should know for now is that he’s a Canadian naturalist, ornithologist, wildlife photographer, conservationist, author, and recently retired lecturer of legendary standing at Carleton.  As an educator and communicator, his joy, obvious wonder, respect, and gentle humour pervade.  I was able to enjoy one of his courses vicariously through Carelton’s CUTV open lecture program, but I learned he’d also written and produced a nature documentary series in the late 90s called <strong><em>Wild by Nature</em></strong>. This turned out to be infuriatingly difficult for me to find online beyond a few snippets, so I eventually mostly gave up hope, returning to the search a couple of times a year since then and reliably renewing my frustrated melancholy.</p>

<p>… Until this summer, when I suddenly found them!  Roland Pirker, one of the fabulous videographers who accompanied Runtz onsite for the series started posting his portfolio on YouTube, among which was (eventually, tortuously) the full thirteen-episode run.  It requites my archival/acquisitive drive beyond reason to have and watch these videos after so long, but better than that, I was thrilled to share them with my wife and son, and to see Runtz’ approach to appreciating nature available again for a new generation.</p>

<p>Please try them.  Maybe we’ll see you outside!</p>

<h2 id="programme-listing">Programme Listing</h2>

<blockquote class="note smaller" title="Blurbs">
  <p>The episode descriptions below are copied from <a href="https://bceln.ca/sites/default/files/trial-renewals/Can-Core%20AV%20Titles%20Oct%2023%202019.xlsx">a 2019-2020 catalogue file (.xlsx)</a> for McIntyre Media Services’ <a href="https://www.can-core.ca/about-us">Can-Core</a> academic video subscription service, which I found hosted on the <a href="https://bceln.ca/offers-renewals/can-core-offer-2019-2020">British Columbia Electronic Library Network</a>.  I couldn’t find <em>Wild by Nature</em> listed or hosted directly on the site otherwise I’d be linking directly to that, but all of this is to say that I’m not the author of these descriptions.</p>
</blockquote>

<h3 id="episode-01-the-rites-of-spring">Episode 01: The Rites of Spring</h3>

<iframe width="560" height="315" src="https://www.youtube.com/embed/OedfBddBcBs?si=W5WFGEoZoan3bN0A" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>After a long and quiet winter, the northern forests come alive with animal activity in the spring.  We’ll make an early visit to Algonquin Park and take an in-depth look at Spruce Grouse, Moose, and Gray Jays, and some of their spring courtship rituals. Wild By Nature is the brainchild of naturalist Michael Runtz, who has dedicated his life to the study and interpretation of the Canadian outdoors.</p>

<h3 id="episode-02-world-in-a-marsh">Episode 02: World in a Marsh</h3>

<iframe width="560" height="315" src="https://www.youtube.com/embed/U5K8SgHnG2Q?si=ooLXHs0pyVhCQaej" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>A marsh is a rich habitat supporting a wide array of fascinating plants and animals.  In this episode, we’ll visit several marshes, including one that is man-made, and have a look at some of their inhabitants including Common Moorhens, Ruddy Ducks, Muskrats, Virginia Rails, and Red-winged Blackbirds. Wild By Nature is the brainchild of naturalist Michael Runtz, who has dedicated his life to the study and interpretation of the Canadian outdoors.</p>

<h3 id="episode-03-due-south">Episode 03: Due South</h3>

<iframe width="560" height="315" src="https://www.youtube.com/embed/VUqrivwDpLY?si=JCtHZ_BUY-EQMiKa" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>The Great Lakes cradle the most southern part of Canada, providing it with a warm climate that allows southern plants and animals to survive there. In this episode we visit one of the world’s most famous bird migration sites, Point Pelee National Park as well as Rondeau Provincial Park and Pelee Island, where the endangered Blue Racer snake resides. Wild By Nature is the brainchild of Michael Runtz, who has dedicated his life to the study and interpretation of the Canadian outdoors.</p>

<h3 id="episode-04-oasis-of-life">Episode 04: Oasis of Life</h3>

<iframe width="560" height="315" src="https://www.youtube.com/embed/rSdbAZW8nAY?si=fOj8A9KWpu7eR34I" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>The Beaver is one of the most amazing animals in Canada, one that creates its own distinct living environment.  In this episode, we’ll examine the amazing diversity of life found in a beaver pond, both in the water and in the dead trees standing in the pond. Wild By Nature is the brainchild of naturalist Michael Runtz, who has dedicated his life to the study and interpretation of the Canadian outdoors.</p>

<h3 id="episode-05-beauty-and-the-beasts">Episode 05: Beauty and the Beasts</h3>

<iframe width="560" height="315" src="https://www.youtube.com/embed/YVkfs8N1grY?si=xKvah13Oa5JbfVOj" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>Wildflowers are beautiful plants, they have a lot of devious pollination strategies which include the participation of a wide array of animals and insects. Wild By Nature is the brainchild of Michael Runtz, who has dedicated his life to the study and interpretation of the Canadian outdoors.</p>

<h3 id="episode-06-life-in-high-places">Episode 06: Life in High Places</h3>

<iframe width="560" height="315" src="https://www.youtube.com/embed/LPqqAIEWbeg?si=FiK8JsOEVHD2EwRD" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>In Gros Morne National Park, Newfoundland, we investigate life above the tree line.  The adaptations of high altitude plants to unimpeded wind and cold temperatures will be examined, as well as how animals survive in this inhospitable environment. Wild By Nature is the brainchild of Michael Runtz, who has dedicated his life to the study and interpretation of the Canadian outdoors.</p>

<h3 id="episode-07-worlds-afloat">Episode 07: Worlds Afloat</h3>

<iframe width="560" height="315" src="https://www.youtube.com/embed/go___O57Phw?si=0yD42XOSXVx2Iu6W" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>This episode visits the floating world of bogs and fens, and explores the unusual plants and animals that call these habitats home, including  carnivorous wildflowers. Wild By Nature is the brainchild of naturalist Michael Runtz, who has dedicated his life to the study and interpretation of the Canadian outdoors.</p>

<h3 id="episode-08-the-toothed-ones">Episode 08: The Toothed Ones</h3>

<iframe width="560" height="315" src="https://www.youtube.com/embed/7eF_Sn-uDY8?si=AvY50RvQfpGi_sZp" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>Dragonflies and damselflies may have changed little in appearance in the 250 million years they have been on Earth, yet they have evolved many fascinating behaviors.  This episode explores the flying, feeding, courtship and egg-laying rituals of this diverse and beautiful group of animals. Wild By Nature is the brainchild of naturalist Michael Runtz, who has dedicated his life to the study and interpretation of the Canadian outdoors.</p>

<h3 id="episode-09-the-tides-of-life">Episode 09: The Tides of Life</h3>

<iframe width="560" height="315" src="https://www.youtube.com/embed/CmrqNyBilp8?si=dnTww4Dy962etfaZ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>A million Semipalmated Sandpipers migrate through the Bay of Fundy, Nova Scotia each summer.  In this episode we will explore the relationship between this phenomenal shorebird and its prey, a tiny invertebrate known as a mud shrimp. Wild By Nature is the brainchild of naturalist Michael Runtz, who has dedicated his life to the study and interpretation of the Canadian outdoors.</p>

<h3 id="episode-10-webs-of-intrigue">Episode 10: Webs of Intrigue</h3>

<iframe width="560" height="315" src="https://www.youtube.com/embed/uRGKIp00sYI?si=H6Y0dnh_4Kh9hi8t" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>Viewers meet a diverse array of predatory insects and spiders as we delve into the world of insect predators. With special emphasis on spiders, we will meet some that hunt on the ground, in the air, by ambush, and some that build incredible webs. Wild By Nature is the brainchild of naturalist Michael Runtz, who has dedicated his life to the study and interpretation of the Canadian outdoors.</p>

<h3 id="episode-11-the-howls-of-august">Episode 11: The Howls of August</h3>

<iframe width="560" height="315" src="https://www.youtube.com/embed/8MJK2TvvFH8?si=hGlr7EYeuOpYq_3G" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>In this episode we will explore the mystique associated with timber wolves and their howls. Viewers will learn why wolves howl and how we enjoy, interpret, and exploit these songs of the wild. Wild By Nature is the brainchild of Michael Runtz, who has dedicated his life to the study and interpretation of the Canadian outdoors.</p>

<h3 id="episode-12-the-journey">Episode 12: The Journey</h3>

<iframe width="560" height="315" src="https://www.youtube.com/embed/3Z--Kc1k70M?si=ex7YoKP6f_MOPvWv" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>One of the most intriguing aspects of birds is the incredible journey many undertake each year.  This episode examines migration and the role that bird banding plays in resolving the intricacies of this phenomenon.  Three different types of banding operations are shown; for baby terns, songbirds and hawks. Wild By Nature is the brainchild of Michael Runtz, who has dedicated his life to the study and interpretation of the Canadian outdoors.</p>

<h3 id="episode-13-the-rut">Episode 13: The Rut</h3>

<iframe width="560" height="315" src="https://www.youtube.com/embed/3Z--Kc1k70M?si=ex7YoKP6f_MOPvWv" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>Moose are one of the largest animals in the world and possess a very complex life history. We will look at the most compelling aspect of this animal’s life - the fall mating behaviour known as the rut. In this episode, Michael finds and interprets moose and the signs of the rut, and calls in an amorous bull.</p>]]></content><author><name>Danny Fekete</name><email>danny@neckdeep.dev</email></author><category term="blog" /><category term="nature, Michael Runtz, Wild by Nature, natural history, tv, documentaries, education, resource, youtube, Roland Pirker" /><summary type="html"><![CDATA[A spectacular, lost series of Canadian natural history documentaries resurfaced recently and deserves your eyes.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://neckdeep.dev/assets/blog/img/runtz-wild-by-nature.png" /><media:content medium="image" url="https://neckdeep.dev/assets/blog/img/runtz-wild-by-nature.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">The Non-Computer Science behind Genetic Algorithms</title><link href="https://neckdeep.dev/blog/2023-06-05-the-non-computer-science-behind-genetic-algorithms/" rel="alternate" type="text/html" title="The Non-Computer Science behind Genetic Algorithms" /><published>2023-06-05T00:00:00+00:00</published><updated>2023-06-05T20:09:39+00:00</updated><id>https://neckdeep.dev/blog/the-non-computer-science-behind-genetic-algorithms</id><content type="html" xml:base="https://neckdeep.dev/blog/2023-06-05-the-non-computer-science-behind-genetic-algorithms/"><![CDATA[<p class="figcaption"><strong>Image Credit:</strong> DALL·E’s attempt at “‘<a href="https://commons.wikimedia.org/wiki/File:Darwin_tree.png">Darwin’s tree of life</a>’ made of dna as a sketch on yellowing paper,” why not?  I actually got to see the original on display at the <a href="https://www.rom.on.ca/en">ROM</a>, ages ago.  Goosebumps!</p>

<blockquote class="note smaller" title="Tim Time">
  <p>This article can function as a companion to Tim John’s work, <a href="https://timjohns.ca/build-your-own-genetic-algorithm.html">“Build your own Genetic Algorithm”</a>.</p>

  <p>Read this <em>before</em> Tim’s article if you’d like to prime yourself on natural selection and genetics: hopefully, it will enrich the impact of the metaphors adopted on the programming-end.  Or, read this article <em>after</em> Tim’s article if you’d like to see how good Tim’s choices of terms were, and how much meaning they embodied.  Or, just read this article if you’d like a refresher on natural selection and genetics.  Do what you want!  I’m not your dad.<sup id="fnref:11" role="doc-noteref"><a href="#fn:11" class="footnote" rel="footnote">1</a></sup></p>
</blockquote>

<ul id="markdown-toc">
  <li><a href="#context" id="markdown-toc-context">Context</a></li>
  <li><a href="#approaching-the-genetic-algorithm" id="markdown-toc-approaching-the-genetic-algorithm">Approaching the Genetic Algorithm</a>    <ul>
      <li><a href="#natural-selection" id="markdown-toc-natural-selection">Natural Selection</a></li>
      <li><a href="#genetics" id="markdown-toc-genetics">Genetics</a></li>
    </ul>
  </li>
  <li><a href="#onward-to-the-genetic-algorithm" id="markdown-toc-onward-to-the-genetic-algorithm">Onward, to the Genetic Algorithm</a></li>
</ul>

<h2 id="context">Context</h2>

<p>As a young student, I dreaded math questions that were categorized as “problem-solving”.  Later as a teacher, I was vindicated somewhat to learn that even among students for whom arithmetic is a strength (<em>not</em> me), word-problems and more open-ended questions are frequent stumbling blocks: re-conceptualizing a paragraph of information into a recognizably-formed equation (or as <em>an apparently arbitrary yet uniquely correct series</em> of recognizably-formed equations) requires skills of interpretation, judgement, and strategic familiarity that are not necessary for other practice questions.  It’s also tricky to gently transition from those innocent-looking equations to said paragraphs of doom, so the experience of encountering one often felt jarring and discouraging in the way that riding over the top of a cliff and then smashing into the face of an opposing cliff before then falling fully to my doom between both cliffs …could feel jarring and discouraging.</p>

<p>Learning to program tricked me into loving problem-solving.  Programming, from my neophyte perspective, was about envisioning a desired thing to exist, and then making it exist; moving from point A to B was about solving the problem of my desired thing not existing <em>yet</em>, and the payoff, in addition to the existence of my desired thing, was the sensation of wizardry.</p>

<p>Addictive.  Frustrating.  Usually fun.  Often hard.  And, for some types of problems, inescapably sub-optimal.  Being a neophyte programmer meant that as I increased my literacy of code, I could see, and try to learn from, other people’s more expert solutions: i.e., those embodying more circumspect and insightful re-conceptualizations of my given problem-space.  My best self<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">2</a></sup> could marvel that so many solutions, so many avenues to success, could exist, and were often far more elegant than anything I had imagined.  Those “endless forms most beautiful and most wonderful” were reminiscent of studying natural selection in university: a space boasting multiple viable approaches to success and a resultant panoply of varied and fascinating artifacts of that striving.</p>

<p>It was therefore a joy to understand that computers could simulate that process in general (of course they could), that they could do so to reach and optimize solutions to arbitrary, possibly silly problems (cf. <a href="https://www.youtube.com/watch?v=qv6UVOQ0F44">MarI/O</a>), and that the solutions achieved could involve approaches <a href="https://nosweatshakespeare.com/quotes/famous/there-are-more-things-in-heaven-and-earth/">not dreamt of in my philosophy</a>.  The pieces fell together for me when <a href="https://timjohns.ca">Tim</a> shared <a href="http://ai-junkie.com/ga/intro/gat1.html">AI-Junkie’s description of the Genetic Algorithm</a>.  Tim recently wrote <a href="https://timjohns.ca/build-your-own-genetic-algorithm.html">an extensive article</a> to modernize AI-Junkie’s work and put his own spin on it, and very kindly invited me to gush my amateur evolution and genetics knowledge as part of the introduction: while seeking to make the coding challenge more inviting, he also wanted to foreground the fun of programming a simulation of the natural world, so being as faithful to the associated disciplines’ terminology as possible was a priority.  …But, as we worked, and the two halves of the article inevitably expanded, it became clear that joining them would leave potential readers (and especially students) with so much material that the cognitive load of keeping it all straight from start to finish would undermine the enhanced clarity and accessibility he was trying to achieve in the first place.<sup id="fnref:10" role="doc-noteref"><a href="#fn:10" class="footnote" rel="footnote">3</a></sup>  So, after ensuring that sufficient science-context could be gleaned from his article to satisfy students with more of a priority on programming, we’ve situated <em>my</em> contribution as a sort of “further reading” for interdisciplinary nerds.</p>

<p>Hello, interdisciplinary nerds.  I love you.</p>

<h2 id="approaching-the-genetic-algorithm">Approaching the Genetic Algorithm</h2>
<p>Very broadly, <strong>genetic algorithms</strong> attempt to mimic principles of natural selection and genetics to build optimal solutions to problems.  They involve generating lots of randomized approaches, evaluating those approaches for their degrees of success, and then preferentially selecting and combining the best attempts (while discarding the worst) to see if the resulting subsequent batch contains solutions that are even better.  Rinse and repeat, sprinkling in degrees of randomness and predictability to taste, until a successful solution, or a solution with a desired degree of efficiency, is achieved.</p>

<p>Because this strategy of solution-building is inspired by—and heavily adopts the metaphors of—academic fields outside of computer science, having a baseline understanding of those fields (and their terminology) should make trying to implement your own genetic algorithms much more intuitive.  So, here you go!</p>

<h3 id="natural-selection">Natural Selection</h3>
<blockquote class="note smaller" title="Gratitude">
  <p>While I’m a dilettante about this stuff, my wife, <strong>Carolyn Piccinin</strong>, actually maintains professional mastery of the domain as a genetic counsellor.  In an better world, <em>she’d</em> be the author of the article you’re reading, but as it stands, we’re all fortunate that she at least went over the text and helped zap my most egregious errors.</p>

  <p>(She stridently believes the systematic analogies I use to help with the terminology below would’ve been stronger and clearer if they involved recipe books rather than encyclopaedias, but allows they’re not technically wrong.  As a GC <em>and</em> a mighty cook, she’s in a doubly-good position to know.  Sorry.)</p>
</blockquote>

<p>Charles Darwin and Alfred Russel Wallace described the process of “Evolution by Natural Selection” as a way to make sense of both the stunning variation across organisms seen in the natural world, as well as the subtle but unavoidable patterns that exist between them.  <strong>Evolution</strong> (i.e., change over time) of species had been theorized before Darwin’s time, but lacked a plausible (observable, eventually testable) engine or mechanism.  Natural Selection was the proposed mechanism, and demonstrated that the abundant variety of life we observe would emerge directly and necessarily from the interaction between a few simple, acknowledged principles:</p>

<ul>
  <li><strong>Heredity:</strong> Offspring tend to resemble some combination of their parents (and diminishingly, their more distant ancestors).</li>
  <li><strong>Mutation:</strong> Novel or modified characteristics occasionally show up that do not appear to have belonged to an ancestor, but can still be passed on to offspring like any other trait.</li>
  <li><strong>Competition:</strong> Because resources are limited and typically insufficient to fully satisfy all members of a given population, not all members will reproduce equally—some will thrive and have lots of offspring while others will be less successful by comparison (due to death, inability to find a mate, etc.).
    <ul>
      <li>Competitive advantage as described by an interaction of an organism’s traits with its environment is often called <strong>fitness</strong> (as a classic example, if a giraffe having a longer neck means access to more leaves on the local tall trees than fellow giraffes, and better access to leaves is demonstrated to represent a lower likelihood of dying before it reproduces, the giraffe could be said to be “more fit”).</li>
    </ul>
  </li>
</ul>

<p>Accepting these principles as plausible features of nature, we can anticipate some overarching consequences by exploring interactions between them:</p>

<ul>
  <li><strong>Heredity with Mutation</strong> suggests that a population has a capacity for change beyond just an endless recombination of preexisting traits.</li>
  <li><strong>Heredity with Competition</strong> suggests that the winners—those able to become parents before they die—will create a new generation that tends to more resemble themselves (specifically, that has a higher proportion of their traits).  Conversely, traits characteristic of the losers—those who died before they reproduced—will fade from the population through subsequent generations.</li>
  <li><strong>Mutation with Competition</strong> suggests that occasionally a novel trait will arise that confers a competitive advantage (e.g., the organism that has that new trait is better able to survive and attract a mate to reproduce because of it).  These traits will then enter and proliferate across the population through subsequent generations.</li>
</ul>

<p>That’s it!  In broad strokes, that engine—the consistent interaction of heredity, mutation, and competition—is the explanation for how the traits of populations change over time, or <em>evolve</em>.  It’s called “<em>Natural</em> Selection” because the “decision” of which traits propagate in a population over time isn’t made by anyone or anything, but is a <em>natural</em> consequence of individual fitness.<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">4</a></sup></p>

<h3 id="genetics">Genetics</h3>

<p>Darwin and Wallace deduced Natural Selection from observable phenomena, and then worked to collect observations that would test, qualify, and ultimately reinforce that framework.  Their theory proposed a <em>mechanism</em> for how changes in species could occur over time, and for how species might splinter as they spread out geographically and definitions of fitness could differ locally.  But crucially, the mechanisms for <em>heredity and mutation themselves</em>—the field of study that would eventually become <strong>genetics</strong>—were unknown to them: roughly in parallel with Darwin, Augustinian monk Gregor Mendel was working to quantify and systematize observable patterns of heredity in peas.<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">5</a></sup>  It wasn’t until 1944 that scientists identified the molecule DNA (deoxyribonucleic acid) as the physical medium to carry information from generation to generation<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">6</a></sup><sup id="fnref:9" role="doc-noteref"><a href="#fn:9" class="footnote" rel="footnote">7</a></sup>.  The mechanics of cellular division, of the (imperfect) reproduction of DNA, of its eventual correspondence with observable, heritable traits emerging from molecular processes inside the cell—this work continues now with ever greater sophistication and specialization as technology and our frameworks of understanding improve.</p>

<p>While natural selection operates at the level of populations, genetics concerns itself with individual members of those populations; some of the foundational ideas are represented in the following terms (organized to work our way from the observable individual organism down to the molecular medium of its heredity):</p>
<ul>
  <li><strong>Organism:</strong> a discrete entity made up of one or more cells, each of which typically contains an identical copy of a genome (described next).
    <ul>
      <li>Organisms with enough features in common to be able to mate and produce viable offspring (i.e., child organisms that can similarly mate and produce viable offspring) are said to be <strong>members of the same species.</strong><sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote" rel="footnote">8</a></sup></li>
      <li>A group of organisms that are members of the same species <em>and</em> are sufficiently local to each other that they can interbreed and compete for the same resources can be said to be <strong>a population.</strong></li>
      <li>Grouping all currently-breeding parents (or the offspring of all currently-breeding parents) within a population into a cohort is the process of describing <strong>a generation.</strong>  Generations in nature often have fuzzy boundaries, but can be conceptually useful as units for tracking the evolution of traits over time.</li>
    </ul>
  </li>
  <li><strong>Genome:</strong> the complete set of data that represents an organism’s heritable features.  In humans, this would be encoded in the full set of an individual’s DNA.
    <ul>
      <li><em>Analogy:</em> An individual’s genome is like the information content in a complete encyclopaedia.</li>
      <li>Identical twins have identical genomes.</li>
    </ul>
  </li>
  <li><strong>Chromosome:</strong> a subdivision of an individual’s genome into a discrete package.  (Humans typically have their genomes packaged into two sets of 23 chromosomes in any given cell)
    <ul>
      <li><em>Analogy:</em> An individual’s chromosomes are like the volumes of an encyclopaedia.</li>
    </ul>
  </li>
  <li><strong>Gene:</strong> a specific region of DNA that represents the instructions for building a particular protein (and ultimately contributes to the expression of a particular observable trait).
    <ul>
      <li><em>Analogy:</em> An individual’s genes are like the individual articles in an encyclopaedia.</li>
      <li><strong>Genes can be viewed as the unit of heredity:</strong> when parents produce offspring through sexual reproduction, those offspring contain a combination of DNA (and ultimately, fitness-impacting genes) drawn from the parents.  If the offspring eventually become parents themselves, those same genes will have a chance to travel to the subsequent generation, and so on.<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">9</a></sup></li>
    </ul>
  </li>
  <li><strong>Allele:</strong> a particular version of a given gene.  The trait ultimately expressed is determined by which version—which allele—is present.
    <ul>
      <li><em>Analogy:</em> The alleles present that govern an individual’s actual eye colour are like different versions of the same article in different encyclopaedias—they may cover the same topic, but with differences in focus, interpretation, etc.<sup id="fnref:8" role="doc-noteref"><a href="#fn:8" class="footnote" rel="footnote">10</a></sup></li>
    </ul>
  </li>
  <li><strong>Nucleotide:</strong> one of a finite set of molecules that can be linked together to form DNA.  The sequence in which these molecules occur <em>is</em> the data which is copied and passed on in part to offspring, and could therefore be considered the fundamental matter of heredity.
    <ul>
      <li><em>Analogy:</em> The nucleotides linked together into an organism’s DNA are like the individual letters in the words in the sentences in the articles in the volumes that make up a complete encyclopaedia.</li>
    </ul>
  </li>
</ul>

<p>When discussing an organism’s traits in genetic terms, it’s helpful to know whether we’re working at the scale of the generally observable, or at the scale of the traits’ molecular underpinnings.  Two more terms are good to have handy, to this end:</p>
<ul>
  <li><strong>Phenotype:</strong> an observable, heritable trait of an individual organism.
    <ul>
      <li>Eye colour is an example of phenotype; which languages are spoken by the organism is <em>not</em> an example of phenotype<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">11</a></sup>.</li>
    </ul>
  </li>
  <li><strong>Genotype:</strong> the specific genes possessed by an individual that are responsible for the expression of an observable, heritable trait.
    <ul>
      <li>The presence of specific alleles of eye-colour-coding genes in an individual’s genome is its eye-colour <em>genotype</em>; these will ultimately interact to result in the <em>phenotype</em> of having an associated eye colour.</li>
    </ul>
  </li>
</ul>

<h2 id="onward-to-the-genetic-algorithm">Onward, to the Genetic Algorithm</h2>

<p>The next step will be to see how these concepts map onto genetic algorithms in programming. <a href="https://timjohns.ca/build-your-own-genetic-algorithm.html">Have fun</a>!</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:11" role="doc-endnote">
      <p>Taran, <em>you</em> can read the articles in whatever order you’d like, too.  I’m a cool dad.  I’ll allow it. <a href="#fnref:11" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:1" role="doc-endnote">
      <p>i.e., when avoiding the bruised ego and self-doubt engendered by witnessing superior execution again and again and again… <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:10" role="doc-endnote">
      <p>cf. the foregoing sentence. <a href="#fnref:10" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>Conversely, <em>artificial</em> selection occurs when populations are bred with intention to encourage or discourage particular traits (and therefore, “fitness” is externally, arbitrarily dictated).  Dog breeds and the modern forms of the fruits and vegetables we eat are classic examples of artificial selection.  (Incidentally, artificial selection in humans is called <a href="https://en.wikipedia.org/wiki/Eugenics">eugenics</a>, and is an endlessly fascinating ethical tire-fire.) <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>Darwin and Mendel don’t appear to have met in person, and while Mendel seems to have sent Darwin his work on heredity in translation, <a href="https://academic.oup.com/qjmed/article/102/8/587/1598792">it’s unclear that Darwin was aware of it during his lifetime</a>.  Facilitating such a meeting is definitely on my time-travelling bucket-list. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p><a href="https://en.wikipedia.org/wiki/DNA">This is a massive oversimplification of what goes on mechanically.</a>.  It’s like saying “Wikipedia is the website that tracks what people think it’s important to publicly write about.” <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:9" role="doc-endnote">
      <p>Watson, Crick, and (increasingly, <a href="https://en.wikipedia.org/wiki/Rosalind_Franklin#Contribution_to_the_model/structure_of_DNA">righteously</a>) Franklin are names that are often associated with the “discovery” of DNA; their contributions in the 1950s have become so overwhelmingly famous because they described the <em>physical structure</em> of the molecule, revealing the elegant <em>mechanism</em> of how it could be replicated despite its incredible complexity. <a href="#fnref:9" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:7" role="doc-endnote">
      <p>Species in nature are not as distinct as they’re often presented and described, since “viability” of offspring can be a matter of degree (excluding cases like mules—horse/donkey hybrids that are born sterile), and can be subject to geographical boundaries.  One of the coolest examples I’ve encountered is the idea of <a href="https://en.wikipedia.org/wiki/Ring_species">ring speciation</a>: imagine a migrating population that arrives at an impassible barrier like a lake or a mountain, and begins to spread around it.  Over multiple generations, local portions of that larger population will be subject to different selection pressures, resulting in local variations building up (and associated, accumulating genetic differences when comparing parts of the population that went one way when it met the barrier, vs. the other).  If the expanding population meets up again on the other side of the barrier (i.e., “closing the ring”), it’s possible that members of the two sides will have built up enough differences that they’ll no longer be genetically compatible with one another—<em>they’ll be different species, by this definition</em>.  Yet, if you were to take sample organisms at smaller geographic intervals, travelling back around that ring from one end to the other, they <em>would</em> be able to interbreed.  Speciation as a gradient!  I love it. <a href="#fnref:7" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:6" role="doc-endnote">
      <p>Richard Dawkins, before his association with atheism, arguably became a household name for his book <em>The Selfish Gene</em>, wherein he explored a fascinating extension of this process and imagined the machinery of organisms—cells, blood, eyes, locomotion, intelligence, tentacles, etc.—as mere vehicles for individual genes to improve their chances of propagation.  It was a fun read, and pre-Creationist-beleaguered Dawkins had a spark of eager excitement that came out in his footnotes especially, that I think the vagaries of the world eventually ground away.  Alas. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:8" role="doc-endnote">
      <p>By neat extension, different encyclopaedias may be more or less successful in different settings due to these choices… <a href="#fnref:8" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p><em>Capacity</em> to speak a language is <em>largely</em> an example of a phenotype (i.e., no cactus is likely to ever speak Urdu no matter how much expert tutelage it has access to, while humans do it all the time). <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Danny Fekete</name><email>danny@neckdeep.dev</email></author><category term="blog" /><category term="programming, nature, genetics, &quot;natural selection&quot;" /><summary type="html"><![CDATA[Validating the joy of simulating natural selection and genetics on my computer with tediously explained science.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://neckdeep.dev/assets/blog/img/dna-tree-of-life.png" /><media:content medium="image" url="https://neckdeep.dev/assets/blog/img/dna-tree-of-life.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Music of the Spheres</title><link href="https://neckdeep.dev/rpd/2023-05-03-music-of-the-spheres/" rel="alternate" type="text/html" title="Music of the Spheres" /><published>2023-05-03T00:00:00+00:00</published><updated>2023-05-09T19:10:25+00:00</updated><id>https://neckdeep.dev/rpd/music-of-the-spheres</id><content type="html" xml:base="https://neckdeep.dev/rpd/2023-05-03-music-of-the-spheres/"><![CDATA[<p>I was able to sit down and get stuck-in properly, resulting in many features!</p>

<h2 id="micro-cores-are-live">Micro-Cores are Live!</h2>

<p><a href="/assets/rpd/downloads/Rock%20Popper%20Deluxe%20-%200.0.23.0504.zip">(Download <code class="language-plaintext highlighter-rouge">Alpha 0.0.23.0504</code> Now!)</a></p>

<p>I completed and implemented the ricocheting Micro-Core spheres I’d wanted to do in Little Steps, and they’re a lot of fun to fight and fly around.  I enhanced other sphere-based waves with simultaneous or eventual introductions of these guys, culminating in a barrage of about 12 of them leading into the final showdown with the larger cores (after about fifteen seconds to clear as many as possible).  They’re slow-moving when not gravitating or ricocheting (making it feasible to swim around them) but have very high mass so they’re not bothered too much by player shots (making them fairly predictable in their movements, until they do-si-do).  I like ‘em!</p>

<p><a href="/assets/rpd/2023-05-03-micro-cores-debut.png"><img src="/assets/rpd/2023-05-03-micro-cores-debut.png" alt="Debut of the Bouncy Micro-Cores" /></a></p>

<h2 id="repulsor-shield-upgradetweak">Repulsor Shield Upgrade/Tweak</h2>

<p>Tweaked repulsor shields again: now, shots that hit players reflect directly (making it reasonably likely that they’ll hit the original shooter, instead of effectively impossible—the powerup is much more useful, now), while shots that hit repulsive enemies still take into account angle of incidence and the surface they hit.  Fighting shielded asteroids, which take many shots and reflect nastily, remains possible with care.</p>

<h2 id="speed-limits-and-zippy-trails">Speed Limits and Zippy-Trails</h2>

<p>I’ve implemented a universal speed limit for entities; earlier, if the player got hit in just the right way (usually by something spawning on top of them, or an unfortunate succession of high-powered bullets to the shields), speed could increase so much that the ship would escape the bounds of the void at all sides of the screen, soft-locking the game.  (I’ve seen this happen to enemies too, making them impossible to finish off to allow the waves to continue.)  Now, everything slows down to a stately <code class="language-plaintext highlighter-rouge">15</code> speed (not noticeable in normal gameplay).</p>

<p>I’ve also implemented optional <code class="language-plaintext highlighter-rouge">target_speed</code> values on entities I’d like to correct for other sources of momentum; the straight-shooter droids now make use of this (allowing me to unify them more closely with <code class="language-plaintext highlighter-rouge">base_enemy</code>, rather than <code class="language-plaintext highlighter-rouge">base_enemy_with_thrusters</code>, as they currently are).  Entities with a <code class="language-plaintext highlighter-rouge">target_speed</code> that are currently decelerating to meet it show a trail: the effect is lots of fun on the Micro-Cores when they gravitate or zoom away.</p>

<h2 id="title-credits">Title Credits</h2>

<p>Finally, for a while I’ve been wanting to display the current version of the game at the start of play, as a sort of seamless title screen (at least, until I get around to implementing a menu system).  I’ve got that set up now: “Rock Popper Deluxe”, a link to this devlog, the version number, and a version title all show up in space-gradients when the game starts, and then quickly fade out to make room for the action.  It’s very, very minimal, but it gives things a slight sense of increased polish.</p>

<p><a href="/assets/rpd/2023-05-03-title-details.png"><img src="/assets/rpd/2023-05-03-title-details.png" alt="Title Credits" /></a></p>]]></content><author><name>Danny Fekete</name><email>danny@neckdeep.dev</email></author><category term="rpd" /><summary type="html"><![CDATA[I was able to sit down and get stuck-in properly, resulting in many features!]]></summary></entry><entry><title type="html">Little Steps</title><link href="https://neckdeep.dev/rpd/2023-04-24-little-steps/" rel="alternate" type="text/html" title="Little Steps" /><published>2023-04-24T00:00:00+00:00</published><updated>2023-05-03T20:51:45+00:00</updated><id>https://neckdeep.dev/rpd/little-steps</id><content type="html" xml:base="https://neckdeep.dev/rpd/2023-04-24-little-steps/"><![CDATA[<p>In mid-December, I started putting in code to support multi-object enemies (hosts and sub-modules, in my terminology).  My goal was to build some procedurally-created asterdroids with armour plates, shield modules, and eventually guns as the first part of this project, but mid-December got hairy (as it does), and then I failed to return to the code, so the vision got stale.  I’d like to get back to it soon, but I’d also like to not let the whole game get mothballed, so I’ve pushed the framework I built and have moved on for the moment, hopefully until I get a bit more momentum.</p>

<p>Speaking of which, I wanted to implement some micro-cores that zip around at a constant speed, but attract each other at a certain threshold, so they’ll do gravitational sling-shots and be an attractive pain to deal with.  I want to see if I can implement a blur-effect on objects moving faster than their target speed, which I think will look good.  And, I’d like these spheres to ricochet off each other if they manage to come in contact, making a tink noise and possibly some pretty particles.  Sprites’re done and imported.</p>

<p><img src="/assets/rpd/2023-04-24-micro-core-sprites.png" alt="Micro-Cores with Authentic Battle Damage!" /></p>

<p>If I like these, I miiiight implement coloured versions with shorter-range beam attacks similar to their original counterparts.</p>

<p><strong>Next step:</strong> build the actual enemy object and make it do those things!</p>

<p>P.S.: I also nerfed Mega Cluster a bit.  Now, none of the spooted asteroids are void persistent (they still need to be carefully avoided when ejected—they just don’t gum up the works as much), and the ejecta-novas fired when damage thresholds are reached only have a 50% chance of being void persistent.  Seemed like if <em>I</em> couldn’t beat the thing persistently, it might’ve been too difficult…</p>]]></content><author><name>Danny Fekete</name><email>danny@neckdeep.dev</email></author><category term="rpd" /><summary type="html"><![CDATA[In mid-December, I started putting in code to support multi-object enemies (hosts and sub-modules, in my terminology). My goal was to build some procedurally-created asterdroids with armour plates, shield modules, and eventually guns as the first part of this project, but mid-December got hairy (as it does), and then I failed to return to the code, so the vision got stale. I’d like to get back to it soon, but I’d also like to not let the whole game get mothballed, so I’ve pushed the framework I built and have moved on for the moment, hopefully until I get a bit more momentum.]]></summary></entry><entry><title type="html">Benchmarking an Old Chestnut: Dead Horse Edition</title><link href="https://neckdeep.dev/blog/2023-03-07-benchmarking-an-old-chestnut-dead-horse-edition/" rel="alternate" type="text/html" title="Benchmarking an Old Chestnut: Dead Horse Edition" /><published>2023-03-07T00:00:00+00:00</published><updated>2023-03-08T02:38:42+00:00</updated><id>https://neckdeep.dev/blog/benchmarking-an-old-chestnut-dead-horse-edition</id><content type="html" xml:base="https://neckdeep.dev/blog/2023-03-07-benchmarking-an-old-chestnut-dead-horse-edition/"><![CDATA[<blockquote class="note smaller" title="Mea Culpa">
  <p>I know, I know: the “beating-a-dead-horse” and “old-chestnut” metaphors mean there’s a horse chestnut joke to be made somewhere, but I haven’t come up with anything worth your time.  Instead, <a href="https://tastehungary.com/journal/the-sweet-gesture-of-gesztenyepure-hungarian-chestnut-puree/">here’s an article about <em>gesztenyepüré</em></a>, the Hungarian chestnut puree of my childhood (just add canned sour cherries for flawless Danny-nostalgia).  Enjoy!</p>
</blockquote>

<ul id="markdown-toc">
  <li><a href="#raymonds-posit" id="markdown-toc-raymonds-posit">Raymond’s Posit</a></li>
  <li><a href="#setting-up-the-board" id="markdown-toc-setting-up-the-board">Setting Up the Board</a></li>
  <li><a href="#results-and-discussion" id="markdown-toc-results-and-discussion">Results and Discussion</a>    <ul>
      <li><a href="#how-right-raymond-was" id="markdown-toc-how-right-raymond-was">How Right Raymond Was</a></li>
      <li><a href="#but-huh" id="markdown-toc-but-huh">But… Huh.</a></li>
      <li><a href="#tally_and_checks-hail-mary-pass" id="markdown-toc-tally_and_checks-hail-mary-pass"><code class="language-plaintext highlighter-rouge">#tally_and_check</code>’s Hail Mary Pass</a></li>
    </ul>
  </li>
</ul>

<h2 id="raymonds-posit">Raymond’s Posit</h2>

<p>A few hours after posting <a href="/blog/2023-03-01-benchmarking-an-old-chestnut/">my previous article</a>, <a href="https://github.com/RayKayy">Raymond Kwan</a>, a classmate from my Lighthouse Labs cohort, reached out with a <em>potentially even geniuser</em> solution to the <a href="https://www.codewars.com/kata/54da5a58ea159efa38000836">oddly-recurring value puzzle</a>:</p>

<blockquote class="note smaller" title="Quoth the Raymond">
  <p>… At the scale that you are testing at got me thinking if removing the second iteration would make any noticeable difference. I believe this can be achieved by instead of tallying, the hashmap just keeps a boolean. e.g. if key (number) does not exist in map then insert; else delete key. This will likely leave you with a hashmap with a size of 1 at the end of your iteration if I understood the constraints correctly. This will increase the number of constant time operations in your initial loop. But wondering if <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mi>n</mi><mo>+</mo><mi>a</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1n + a)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span></span></span></span> will be better than <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>2</mn><mi>n</mi><mo>+</mo><mi>b</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(2n + b)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord">2</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">b</span><span class="mclose">)</span></span></span></span>; where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mo>&gt;</mo><mi>b</mi></mrow><annotation encoding="application/x-tex">a &gt; b</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5782em;vertical-align:-0.0391em;"></span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">b</span></span></span></span>.</p>
</blockquote>

<p>As a quick refresher, my solution was to run through the provided array once, keeping a tally of each value encountered, and then iterate through the tallies to find the one with an odd value.  This amounts to two operations of linear time-complexity (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>) with the size of the test array and the size of the tallies hash (ie., the number of distinct values in the test array) adding up to be the determining factor in the length of the operation.</p>

<p>If I understand Raymond correctly, he figures that since I’m exploring at the scale of multi-million-element test arrays and resultant (potentially) multi-thousand-key tally hashes, there may be meaningful room to optimize the process by eliminating that second iteration over the tallies hash.  His solution is beautiful to me: <strong>don’t keep tallies for the values; just flip a bit for each value when you encounter it.</strong></p>

<p>So, for example, in the array <code class="language-plaintext highlighter-rouge">[ :smurf, :hobbit, :smurf ]</code>:</p>

<ul>
  <li>Start the iteration:
    <ul>
      <li>Encounter a <code class="language-plaintext highlighter-rouge">:smurf</code> , flip the <code class="language-plaintext highlighter-rouge">:smurf</code> bit to <code class="language-plaintext highlighter-rouge">true</code></li>
      <li>Encounter a <code class="language-plaintext highlighter-rouge">:hobbit</code>, flip the <code class="language-plaintext highlighter-rouge">:hobbit</code> bit to <code class="language-plaintext highlighter-rouge">true</code></li>
      <li>Encounter a <code class="language-plaintext highlighter-rouge">:smurf</code>, flip the <code class="language-plaintext highlighter-rouge">:smurf</code> bit to <code class="language-plaintext highlighter-rouge">false</code></li>
    </ul>
  </li>
  <li>Iteration’s done; the only remaining <code class="language-plaintext highlighter-rouge">true</code> bit belongs to <code class="language-plaintext highlighter-rouge">:hobbit</code>, so that must be the oddly-frequent value in the array.  Distribute celebratory <a href="https://lotr.fandom.com/wiki/Pipe-weed">pipe-weed</a>, console <a href="https://en.wikipedia.org/wiki/Gargamel">Gargamel</a>, etc.</li>
</ul>

<p>Because evenly-frequent values will ultimately turn themselves off while odd ones won’t, the answer is the last one standing.  Raymond suggested using a hash to track our flipping bits by creating and deleting keys for the associated values when encountered: in the end, the hash would be left with just one remaining key.  His question is, would the single iteration across the test array (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>) plus multiple constant-time (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span>) operations of…</p>
<ul>
  <li>checking addresses to see if the hash is populated, and then</li>
  <li>creating or deleting the keys as appropriate</li>
  <li>plus the final step of finding the hash’s only key and asking for its name…</li>
</ul>

<p>… be faster than my approach?  And, under what circumstances?</p>

<h2 id="setting-up-the-board">Setting Up the Board</h2>

<p>Here’s how I’ve implemented Raymond’s approach:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">flip_the_bit</span><span class="p">(</span><span class="n">test_array</span><span class="p">)</span>
  <span class="n">value_bits</span> <span class="o">=</span> <span class="p">{</span> <span class="p">}</span>

  <span class="n">test_array</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span>
    <span class="k">if</span> <span class="n">value_bits</span><span class="p">[</span><span class="n">value</span><span class="p">]</span>
      <span class="n">value_bits</span><span class="p">.</span><span class="nf">delete</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
    <span class="k">else</span>
      <span class="n">value_bits</span><span class="p">[</span><span class="n">value</span><span class="p">]</span> <span class="o">=</span> <span class="kp">true</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="n">value_bits</span><span class="p">.</span><span class="nf">first</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">end</span>
</code></pre></div></div>

<p>And, for reference, here’s <em>my</em> approach from the previous article:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">tally_and_check</span><span class="p">(</span><span class="n">test_array</span><span class="p">)</span>
  <span class="n">tallies</span> <span class="o">=</span> <span class="no">Hash</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

  <span class="n">test_array</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span>
    <span class="n">tallies</span><span class="p">[</span><span class="n">value</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
  <span class="k">end</span>

  <span class="n">tallies</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">original_value_from_the_test_array</span><span class="p">,</span> <span class="n">frequency</span><span class="o">|</span>
    <span class="k">if</span> <span class="n">frequency</span><span class="p">.</span><span class="nf">odd?</span>
      <span class="k">return</span> <span class="n">original_value_from_the_test_array</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>I’m hypothesizing that the nature of the test array is going to be especially important: it seems to me that the larger the <code class="language-plaintext highlighter-rouge">tallies</code> hash I need to check, the more significant Raymond’s savings.  So, I need to create some new test-arrays to better exercise this.  Here’s a guide:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center">Range of Possible Values</th>
      <th style="text-align: center">Location of Oddly-Recurring Value</th>
      <th style="text-align: center">Name of Array</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">0..1</code></td>
      <td style="text-align: center">Earliest possible index</td>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">best_case_value_poor</code></td>
    </tr>
    <tr>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">0..1</code></td>
      <td style="text-align: center">Latest possible index</td>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">worst_case_value_poor</code></td>
    </tr>
    <tr>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">1..2000</code></td>
      <td style="text-align: center">Earliest possible index</td>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">best_case_value_mid</code></td>
    </tr>
    <tr>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">1..2000</code></td>
      <td style="text-align: center">Random index</td>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">average_case_value_mid</code></td>
    </tr>
    <tr>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">1..2000</code></td>
      <td style="text-align: center">Latest possible index</td>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">worst_case_value_mid</code></td>
    </tr>
    <tr>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">1..10_000_000</code></td>
      <td style="text-align: center">Earliest possible index</td>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">best_case_value_rich</code></td>
    </tr>
    <tr>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">1..10_000_000</code></td>
      <td style="text-align: center">Random index</td>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">average_case_value_rich</code></td>
    </tr>
    <tr>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">1..10_000_000</code></td>
      <td style="text-align: center">Latest possible index</td>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">worst_case_value_rich</code></td>
    </tr>
  </tbody>
</table>

<p>And, here’s the implementation, again resulting in arrays that are twenty million and one elements long:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Value-Poor Arrays</span>
<span class="n">best_case_value_poor</span> <span class="o">=</span> <span class="p">(</span><span class="mi">0</span><span class="o">..</span><span class="mi">1</span><span class="p">).</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">number</span><span class="o">|</span>
  <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">10_000_000</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="k">end</span><span class="p">.</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">prepend</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

<span class="n">worst_case_value_poor</span> <span class="o">=</span> <span class="p">(</span><span class="mi">0</span><span class="o">..</span><span class="mi">1</span><span class="p">).</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">number</span><span class="o">|</span>
  <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">10_000_000</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="k">end</span><span class="p">.</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>


<span class="c1"># Value-Middling Arrays</span>
<span class="n">average_case_value_mid</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">2000</span><span class="p">).</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">number</span><span class="o">|</span>
  <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">10_000</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="k">end</span><span class="p">.</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span><span class="nb">rand</span><span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">2000</span><span class="p">)).</span><span class="nf">shuffle</span>

<span class="n">best_case_value_mid</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">2000</span><span class="p">).</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">number</span><span class="o">|</span>
  <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">10_000</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="k">end</span><span class="p">.</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">prepend</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>

<span class="n">worst_case_value_mid</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">2000</span><span class="p">).</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">number</span><span class="o">|</span>
  <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">10_000</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="k">end</span><span class="p">.</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span><span class="mi">2000</span><span class="p">)</span>


<span class="c1"># Value-Rich Arrays</span>
<span class="n">average_case_value_rich</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">10_000_000</span><span class="p">).</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">number</span><span class="o">|</span>
  <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="k">end</span><span class="p">.</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span><span class="nb">rand</span><span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">10_000_000</span><span class="p">)).</span><span class="nf">shuffle</span>

<span class="n">best_case_value_rich</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">10_000_000</span><span class="p">).</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">number</span><span class="o">|</span>
  <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="k">end</span><span class="p">.</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">prepend</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>

<span class="n">worst_case_value_rich</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">10_000_000</span><span class="p">).</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">number</span><span class="o">|</span>
  <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="k">end</span><span class="p">.</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span><span class="mi">10_000_000</span><span class="p">)</span>
</code></pre></div></div>

<blockquote class="note smaller" title="Fun Fact">
  <p>It takes my system about 30 seconds and 10% of its RAM to instantiate these examples before the benchmark even starts up.</p>
</blockquote>

<p>Penultimately, here’s my benchmark setup:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'benchmark'</span>

<span class="no">Benchmark</span><span class="p">.</span><span class="nf">bmbm</span> <span class="k">do</span> <span class="o">|</span><span class="n">scenario</span><span class="o">|</span>
  <span class="c1">###  Tally and Check Scenarios  ###</span>
  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'TallyCheck: Poor, Best'</span><span class="p">)</span> <span class="p">{</span> <span class="n">tally_and_check</span><span class="p">(</span><span class="n">best_case_value_poor</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'TallyCheck: Poor, Worst'</span><span class="p">)</span> <span class="p">{</span> <span class="n">tally_and_check</span><span class="p">(</span><span class="n">worst_case_value_poor</span><span class="p">)</span> <span class="p">}</span>

  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'TallyCheck: Mid, Average'</span><span class="p">)</span> <span class="p">{</span> <span class="n">tally_and_check</span><span class="p">(</span><span class="n">average_case_value_mid</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'TallyCheck: Mid, Best'</span><span class="p">)</span> <span class="p">{</span> <span class="n">tally_and_check</span><span class="p">(</span><span class="n">best_case_value_mid</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'TallyCheck: Mid, Worst'</span><span class="p">)</span> <span class="p">{</span> <span class="n">tally_and_check</span><span class="p">(</span><span class="n">worst_case_value_mid</span><span class="p">)</span> <span class="p">}</span>

  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'TallyCheck: Rich, Average'</span><span class="p">)</span> <span class="p">{</span> <span class="n">tally_and_check</span><span class="p">(</span><span class="n">average_case_value_rich</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'TallyCheck: Rich, Best'</span><span class="p">)</span> <span class="p">{</span> <span class="n">tally_and_check</span><span class="p">(</span><span class="n">best_case_value_rich</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'TallyCheck: Rich, Worst'</span><span class="p">)</span> <span class="p">{</span> <span class="n">tally_and_check</span><span class="p">(</span><span class="n">worst_case_value_rich</span><span class="p">)</span> <span class="p">}</span>

  <span class="c1">###  Flip the Bit Scenarios  ###</span>
  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'FlipBit: Poor, Best'</span><span class="p">)</span> <span class="p">{</span> <span class="n">flip_the_bit</span><span class="p">(</span><span class="n">best_case_value_poor</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'FlipBit: Poor, Worst'</span><span class="p">)</span> <span class="p">{</span> <span class="n">flip_the_bit</span><span class="p">(</span><span class="n">worst_case_value_poor</span><span class="p">)</span> <span class="p">}</span>

  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'FlipBit: Mid, Average'</span><span class="p">)</span> <span class="p">{</span> <span class="n">flip_the_bit</span><span class="p">(</span><span class="n">average_case_value_mid</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'FlipBit: Mid, Best'</span><span class="p">)</span> <span class="p">{</span> <span class="n">flip_the_bit</span><span class="p">(</span><span class="n">best_case_value_mid</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'FlipBit: Mid, Worst'</span><span class="p">)</span> <span class="p">{</span> <span class="n">flip_the_bit</span><span class="p">(</span><span class="n">worst_case_value_mid</span><span class="p">)</span> <span class="p">}</span>

  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'FlipBit: Rich, Average'</span><span class="p">)</span> <span class="p">{</span> <span class="n">flip_the_bit</span><span class="p">(</span><span class="n">average_case_value_rich</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'FlipBit: Rich, Best'</span><span class="p">)</span> <span class="p">{</span> <span class="n">flip_the_bit</span><span class="p">(</span><span class="n">best_case_value_rich</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'FlipBit: Rich, Worst'</span><span class="p">)</span> <span class="p">{</span> <span class="n">flip_the_bit</span><span class="p">(</span><span class="n">worst_case_value_rich</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>

<h2 id="results-and-discussion">Results and Discussion</h2>

<p>Building the test-arrays was by far the most intense thing my computer had to do during the benchmarking process.  After about two minutes, here were the results:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>                                user     system      total        real
TallyCheck: Poor, Best      1.718976   0.001031   1.720007 (  1.729708)
TallyCheck: Poor, Worst     1.726422   0.000014   1.726436 (  1.737059)

TallyCheck: Mid, Average    2.217027   0.000038   2.217065 (  2.227950)
TallyCheck: Mid, Best       1.996516   0.000033   1.996549 (  2.006564)
TallyCheck: Mid, Worst      2.003764   0.000009   2.003773 (  2.016251)

TallyCheck: Rich, Average   6.929435   0.355002   7.284437 (  7.314630)
TallyCheck: Rich, Best      4.645296   0.342828   4.988124 (  5.013641)
TallyCheck: Rich, Worst     5.132136   0.340092   5.472228 (  5.496994)


FlipBit: Poor, Best         1.660526   0.000000   1.660526 (  1.670883)
FlipBit: Poor, Worst        1.578161   0.000000   1.578161 (  1.588549)

FlipBit: Mid, Average       2.354981   0.000027   2.355008 (  2.366234)
FlipBit: Mid, Best          1.710901   0.000004   1.710905 (  1.721515)
FlipBit: Mid, Worst         1.631275   0.000000   1.631275 (  1.643420)

FlipBit: Rich, Average      6.524084   0.307339   6.831423 (  6.859530)
FlipBit: Rich, Best         1.754409   0.000009   1.754418 (  1.764125)
FlipBit: Rich, Worst        1.628670   0.000040   1.628710 (  1.638146)
</code></pre></div></div>

<p>Or, in handy table format:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center"><strong>Scenario</strong></th>
      <th style="text-align: center"><strong><code class="language-plaintext highlighter-rouge">#tally_and_check</code></strong></th>
      <th style="text-align: center"><strong><code class="language-plaintext highlighter-rouge">#flip_the_bit</code></strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">Value-Poor, Best-Case</td>
      <td style="text-align: center">1.73 seconds</td>
      <td style="text-align: center">1.67 seconds</td>
    </tr>
    <tr>
      <td style="text-align: center">Value-Poor, Worst-Case</td>
      <td style="text-align: center">1.74 seconds</td>
      <td style="text-align: center">1.59 seconds</td>
    </tr>
    <tr>
      <td style="text-align: center">Value-Middling, Average-Case</td>
      <td style="text-align: center">2.23 seconds</td>
      <td style="text-align: center">2.37 seconds</td>
    </tr>
    <tr>
      <td style="text-align: center">Value-Middling, Best-Case</td>
      <td style="text-align: center">2.01 seconds</td>
      <td style="text-align: center">1.72 seconds</td>
    </tr>
    <tr>
      <td style="text-align: center">Value-Middling, Worst-Case</td>
      <td style="text-align: center">2.02 seconds</td>
      <td style="text-align: center">1.64 seconds</td>
    </tr>
    <tr>
      <td style="text-align: center">Value-Rich, Average-Case</td>
      <td style="text-align: center">7.31 seconds</td>
      <td style="text-align: center">6.86 seconds</td>
    </tr>
    <tr>
      <td style="text-align: center">Value-Rich, Best-Case</td>
      <td style="text-align: center">5.01 seconds</td>
      <td style="text-align: center">1.76 seconds</td>
    </tr>
    <tr>
      <td style="text-align: center">Value-Rich, Worst-Case</td>
      <td style="text-align: center">5.50 seconds</td>
      <td style="text-align: center">1.64 seconds</td>
    </tr>
  </tbody>
</table>

<h3 id="how-right-raymond-was">How Right Raymond Was</h3>

<p>In nearly<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> all cases, Raymond’s approach achieved faster times.  Particularly, for all of the best- and worst- case scenarios, the time for <code class="language-plaintext highlighter-rouge">#flip_the_bit</code> was constant at around 1.7 seconds: as expected, his method doesn’t care how many distinct values the array has; driving to a specific hash key to make changes, and getting the oddly-frequent value at the end of the process, are both constant-time, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span> operations.</p>

<p>Also as expected, <code class="language-plaintext highlighter-rouge">#tally_and_check</code> <em>did</em> care about the range of possible values in the array, since it resulted in a larger hash to check after the initial run-through.  Moreover, even in the value-poor scenarios where Raymond’s bit-flipping is up against a <code class="language-plaintext highlighter-rouge">tallies</code>-iteration with a hash only two keys large (so, the <em>best-case</em> best-case scenario for my method), he still consistently beats me.</p>

<p>I think this provides the clearest answer to his question of whether knocking out an unnecessary linear-time operation at the cost of some additional constant-time operations is still faster than two <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span> operations: for my implementations of this benchmark, that’s a <strong>yup</strong>.  Bravo!</p>

<h3 id="but-huh">But… Huh.</h3>

<p>There was one unexpected pattern that emerged from my experiment: at all levels of value-richness, the <em>average-case</em> arrays took <em>longer</em> to process than the best- and worst-case equivalents.  What I’d anticipated was that the value would fall somewhere between the two extremes; given the counter-intuitive results, I’m worried I’ve set an unfair test.</p>

<p>The most obvious, consistent difference in the scenarios is that the average-case arrays are <em>shuffled</em> and the best- and worst-case arrays are <em>sorted</em>.  Initially, then, I wondered if Ruby attempts to sort the arrays behind the scene prior to the <code class="language-plaintext highlighter-rouge">#find</code> iteration in <code class="language-plaintext highlighter-rouge">#tally_and_check</code>, adding a chunk of time for those scenarios… but <code class="language-plaintext highlighter-rouge">#flip_the_bit</code> doesn’t use <code class="language-plaintext highlighter-rouge">#find</code>, yet it exhibits the same slowdown.  Could Ruby be sorting the array before even a basic <code class="language-plaintext highlighter-rouge">#each</code> sweep, just as a matter of course?  That seems extravagant.</p>

<p>My other thought was that maybe there’s some extra efficiency to accessing the same physical space in memory over-and-over for <em>either</em> tallying <em>or</em> key building/destroying, which might be what happens with a sorted array, but not with a shuffled array: in sorted-array scenarios, <code class="language-plaintext highlighter-rouge">tallies</code> increments a value stored <em>in the same key over and over again</em>, and maybe something similar happens to <code class="language-plaintext highlighter-rouge">value_bits</code>?  I’d love to know for sure.</p>

<p>… But for now, it makes me wonder, could <em>this</em> be the edge <code class="language-plaintext highlighter-rouge">#tally_and_check</code> needs to beat out <code class="language-plaintext highlighter-rouge">#flip_the_bit</code>?  What if our value-poor arrays were pre-shuffled?!</p>

<h3 id="tally_and_checks-hail-mary-pass"><code class="language-plaintext highlighter-rouge">#tally_and_check</code>’s Hail Mary Pass</h3>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">shuffled_value_poor</span> <span class="o">=</span> <span class="p">(</span><span class="mi">0</span><span class="o">..</span><span class="mi">1</span><span class="p">).</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">number</span><span class="o">|</span>
  <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">10_000_000</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="k">end</span><span class="p">.</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span><span class="nb">rand</span><span class="p">(</span><span class="mi">0</span><span class="o">..</span><span class="mi">1</span><span class="p">)).</span><span class="nf">shuffle</span>

<span class="no">Benchmark</span><span class="p">.</span><span class="nf">bmbm</span> <span class="k">do</span> <span class="o">|</span><span class="n">scenario</span><span class="o">|</span>
  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'TallyCheck: Poor, Shuffled'</span><span class="p">)</span> <span class="p">{</span> <span class="n">tally_and_check</span><span class="p">(</span><span class="n">shuffled_value_poor</span><span class="p">)</span> <span class="p">}</span>

  <span class="n">scenario</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'FlipBit: Poor, Shuffled'</span><span class="p">)</span> <span class="p">{</span> <span class="n">flip_the_bit</span><span class="p">(</span><span class="n">shuffled_value_poor</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>                                 user     system      total        real
TallyCheck: Poor, Shuffled   1.893808   0.000000   1.893808 (  1.904579)
FlipBit: Poor, Shuffled      1.621908   0.000000   1.621908 (  1.626906)
</code></pre></div></div>

<p>Nope: <code class="language-plaintext highlighter-rouge">#flip_the_bit</code> is just consistently better, and it leaves me confused about what could be causing the slowdown when processing the average-case scenarios.  If anyone knows and would like to enlighten me, I’d be chuffed.</p>

<p>Meanwhile, thanks for the good clean fun, Raymond!</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>The Value-Middling, Average-Case (with shuffled arrays) always came out a little in favour of <code class="language-plaintext highlighter-rouge">#tally_and_check</code>—I have <em>no</em> idea why.  I tried it about ten times. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Danny Fekete</name><email>danny@neckdeep.dev</email></author><category term="blog" /><category term="programming, math, optimization" /><summary type="html"><![CDATA[One more pass on the oddly-recurring value puzzle. There's always a more optimal method.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://neckdeep.dev/assets/blog/img/rodin-chestnut-horse.png" /><media:content medium="image" url="https://neckdeep.dev/assets/blog/img/rodin-chestnut-horse.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Benchmarking an Old Chestnut</title><link href="https://neckdeep.dev/blog/2023-03-01-benchmarking-an-old-chestnut/" rel="alternate" type="text/html" title="Benchmarking an Old Chestnut" /><published>2023-03-01T00:00:00+00:00</published><updated>2023-03-08T02:38:42+00:00</updated><id>https://neckdeep.dev/blog/benchmarking-an-old-chestnut</id><content type="html" xml:base="https://neckdeep.dev/blog/2023-03-01-benchmarking-an-old-chestnut/"><![CDATA[<blockquote class="note smaller" title="Spoiler Warning">
  <p>This article will discuss solutions to a specific Code Wars kata (but the <em>type</em> of question is fairly common).  If you’d prefer not to have the solution spoiled and want to try it yourself first, <a href="https://www.codewars.com/kata/54da5a58ea159efa38000836">here’s the puzzle</a>.</p>
</blockquote>

<p>Towards the end of my bootcamp, when everyone was crunching on their final projects and really starting to grapple with the symptoms of adrenaline-occluded sleep deprivation, <a href="https://www.lighthouselabs.ca/">Lighthouse Labs</a> shifted their mandatory morning lecture series over from material that contextualized our coursework to the stuff our mentors were <em>actually</em> interested in talking about: <a href="https://elm-lang.org/">Elm</a> and functional programming, content delivery network optimization and database sharding, cryptography, time-complexity optimization—comparatively abstract topics that would have been among the most engaging of the entire curriculum if I hadn’t been helplessly sleeping through most of them.</p>

<p>Last week, I was pleasantly surprised to discover that a bit of the optimization lecture had somehow permeated and persisted.  At <a href="https://timjohns.ca">Tim</a>’s recommendation, I was having a stab at solving a Code Wars kata while trying to coherently verbalize my thought-process (my perennial tech-interview bugaboo).  We were working on <a href="https://www.codewars.com/kata/54da5a58ea159efa38000836">this one</a>.</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(quoted from https://www.codewars.com/kata/54da5a58ea159efa38000836)

Given an array of integers, find the one that appears an odd number of times.

There will always be only one integer that appears an odd number of times.
Examples

[7] should return 7, because it occurs 1 time (which is odd).
[0] should return 0, because it occurs 1 time (which is odd).
[1,1,2] should return 2, because it occurs 1 time (which is odd).
[0,1,0,1,0] should return 0, because it occurs 3 times (which is odd).
[1,2,2,3,3,3,4,3,3,3,2,2,1] should return 4, because it appears 1 time (which is odd).
</code></pre></div></div>

<p>Competing half-formed strategies swarmed me, most of which were smothered by self-doubt and the fear that I was embarrassingly selecting (and at risk of publicly promoting) <em>The Naïve Approach</em>.  Finally, the mortification of my <a href="https://en.wikipedia.org/wiki/Dead_air">dead air</a> won out and I took a stab, couching it with something sufficiently <a href="https://en.wikipedia.org/wiki/Weasel_word">weaselly</a> like “okay, I think, maybe, to start, a naïve approach <em>could</em> be…”.  In the debrief afterwards, it turned out that my approach was optimal (suspicious); the more conventional approach hadn’t occurred to me as it had when I’d been sitting (slumping) through the Lighthouse Labs lecture, suggesting I’d internalized better practices successfully and without my knowledge.  Hooray…?</p>

<p>Anyhoo, if <em>I</em> understand how to do this, it’s got to be the first thing <em>anyone</em> learns about the subject whenever they’re exposed to it.  But, if you’re interested, I’d love to walk you through the different conventional solutions for this puzzle as well as the benchmarking process I implemented just to see how useful my unexpected competence could be.</p>

<ul id="markdown-toc">
  <li><a href="#the-naïve-approach" id="markdown-toc-the-naïve-approach">The Naïve Approach</a></li>
  <li><a href="#my-mind-bendingly-genius-approach" id="markdown-toc-my-mind-bendingly-genius-approach">My Mind-Bendingly Genius Approach</a></li>
  <li><a href="#but-really" id="markdown-toc-but-really">But, really…?</a></li>
  <li><a href="#benchmark-bumbum" id="markdown-toc-benchmark-bumbum">Benchmark Bumbum</a>    <ul>
      <li><a href="#i-knew-it" id="markdown-toc-i-knew-it">I knew it!</a></li>
      <li><a href="#best-case-scenario" id="markdown-toc-best-case-scenario">Best-Case Scenario</a></li>
      <li><a href="#worst-case-scenario" id="markdown-toc-worst-case-scenario">Worst-Case Scenario</a></li>
    </ul>
  </li>
  <li><a href="#moar-numbers" id="markdown-toc-moar-numbers">Moar Numbers</a>    <ul>
      <li><a href="#best-case-scenario-again" id="markdown-toc-best-case-scenario-again">Best-Case Scenario Again</a></li>
      <li><a href="#worst-case-scenario-again" id="markdown-toc-worst-case-scenario-again">Worst-Case Scenario Again</a></li>
    </ul>
  </li>
  <li><a href="#fine-then" id="markdown-toc-fine-then">Fine, then.</a></li>
</ul>

<h2 id="the-naïve-approach">The Naïve Approach</h2>
<p>Right: we want to find out if a given value in an array occurs an odd number of times (with the kind proviso that only one element in the provided inputs will ever be odd).  That means we’ll want to iterate over the array at least once to check the elements:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">find_the_oddly_populated_value</span><span class="p">(</span><span class="n">test_array</span><span class="p">)</span>
  <span class="n">test_array</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span>
    <span class="c1"># Something good.</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>When we encounter a value, we then need to figure out how many times it occurs in the array so we can check whether that number is odd or even.  So, at this point, we <em>could</em> count how many times the current value comes up, check to see if that number is odd, and then report our success or move on to the next element:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">find_and_count</span><span class="p">(</span><span class="n">test_array</span><span class="p">)</span>
  <span class="n">test_array</span><span class="p">.</span><span class="nf">find</span> <span class="p">{</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span> <span class="n">test_array</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="n">value</span><span class="p">).</span><span class="nf">odd?</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Idiomatic Ruby makes this look like a clean, snappy process and reads like human language, but it obscures what an expensive operation it is that we’re actually doing.  Ultimately, <code class="language-plaintext highlighter-rouge">#find</code> and <code class="language-plaintext highlighter-rouge">#count</code> <em>both</em> iterate over the array, so the following code is roughly equivalent:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">find_and_count</span><span class="p">(</span><span class="n">test_array</span><span class="p">)</span>
  <span class="n">test_array</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">current_value_from_first_pass</span><span class="o">|</span>
    <span class="n">hits</span> <span class="o">=</span> <span class="mi">0</span>

    <span class="n">test_array</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">possible_equivalent_value_from_second_pass</span><span class="o">|</span>
      <span class="k">if</span> <span class="n">current_value_from_first_pass</span> <span class="o">==</span> <span class="n">possible_equivalent_value_from_second_pass</span>
        <span class="n">hits</span> <span class="o">+=</span> <span class="mi">1</span>
      <span class="k">end</span>
    <span class="k">end</span>

    <span class="k">return</span> <span class="n">current_value_from_first_pass</span> <span class="k">if</span> <span class="n">hits</span><span class="p">.</span><span class="nf">odd?</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>For each element in the array, we’re checking the array an extra time.  An array, <code class="language-plaintext highlighter-rouge">[ 1, 1, 2 ]</code>, with three elements would each require us checking three more elements for a total of nine (or 12, if you count the initial iteration).  If the array had eleven elements…</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span> <span class="p">]</span>
</code></pre></div></div>

<p>… We’d be iterating 10 extra times, checking 110 elements total.  The amount of time needed, while very very tiny on modern systems for small arrays, increases exponentially (quadratically, in this case) as the length of the array increases.  For arrays with millions of elements, or where the operation at each step is more taxing than just checking equality<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">1</a></sup>, this becomes a problem.  Computer science borrows from math to describe this property of <a href="https://en.wikipedia.org/wiki/Time_complexity">“time complexity”</a> using <a href="https://en.wikipedia.org/wiki/Big_O_notation">“big O notation”</a>; in our case, if the time to complete an operation increases as the square of the size of the input, we’d call it “quadratic time complexity” and depict it as <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n^2)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0641em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>.</p>

<h2 id="my-mind-bendingly-genius-approach">My Mind-Bendingly Genius Approach</h2>

<p>The solution I came up with<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">2</a></sup> was to build a second data structure into which I could optimally reorganize <code class="language-plaintext highlighter-rouge">test_array</code>.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">tally_and_check</span><span class="p">(</span><span class="n">test_array</span><span class="p">)</span>
  <span class="c1"># Step 1</span>
  <span class="n">tallies</span> <span class="o">=</span> <span class="no">Hash</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

  <span class="c1"># Step 2</span>
  <span class="n">test_array</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span>
    <span class="n">tallies</span><span class="p">[</span><span class="n">value</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
  <span class="k">end</span>

  <span class="c1"># Step 3</span>
  <span class="n">tallies</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">original_value_from_the_test_array</span><span class="p">,</span> <span class="n">frequency</span><span class="o">|</span>
    <span class="k">if</span> <span class="n">frequency</span><span class="p">.</span><span class="nf">odd?</span>
      <span class="k">return</span> <span class="n">original_value_from_the_test_array</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<ul>
  <li><strong>Step 1:</strong> I created a <code class="language-plaintext highlighter-rouge">tallies</code> hash (more generally, a dictionary) with a default value of <code class="language-plaintext highlighter-rouge">0</code> for undefined keys (in Ruby, <code class="language-plaintext highlighter-rouge">Hash.new(0)</code>).  <code class="language-plaintext highlighter-rouge">tallies</code>’s <em>keys</em> in this case would be the distinct values in the test array, and <code class="language-plaintext highlighter-rouge">tallies</code>’s <em>values</em> would be the frequency with which they were encountered.</li>
  <li><strong>Step 2:</strong> I iterated over <code class="language-plaintext highlighter-rouge">test_array</code> <em>once</em>, incrementing <code class="language-plaintext highlighter-rouge">tallies</code> at the key corresponding with the current value.  In the end, I might wind up with a hash that looked like this:
    <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="ss">smurfs: </span><span class="mi">6</span><span class="p">,</span>
  <span class="ss">hobbits: </span><span class="mi">2</span><span class="p">,</span>
  <span class="ss">dwarves: </span><span class="mi">8</span><span class="p">,</span>
  <span class="ss">nac_mac_feegle: </span><span class="mi">19</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li><strong>Step 3:</strong> Finally, iterate over the <code class="language-plaintext highlighter-rouge">tallies</code> hash, checking each value (<code class="language-plaintext highlighter-rouge">frequency</code>) for oddness, and upon finding one, returning the key (<code class="language-plaintext highlighter-rouge">original_value_from_the_test_array</code>) as the output of the function (and my final answer).</li>
</ul>

<p>When I first learned this, I remember despairing (sleepily) that a whole bunch of apparent and unintuitive complexity was being introduced to solve the problem.  The key benefit, though, is that I’m iterating over <code class="language-plaintext highlighter-rouge">test_array</code> <em>only once</em>, and then iterating over <code class="language-plaintext highlighter-rouge">tallies</code> <em>only once</em>.  Both operations have <em>linear time complexity</em> (represented as <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>) resulting in total time taken that grows at a constant rate as a function of the length of the input (which is fair) rather than exponentially (which is catastrophic).<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">3</a></sup></p>

<h2 id="but-really">But, really…?</h2>

<p>Top solutions on Code Wars for this kata resembled the naïve approach, so even though Tim had given me his blessing, I wanted to see if maybe I could still be wrong somehow:</p>
<ul>
  <li>It seemed possible that Ruby’s <code class="language-plaintext highlighter-rouge">#find</code> or <code class="language-plaintext highlighter-rouge">#count</code> methods might involve some fancy optimizations under the hood that would get me better than expected results.</li>
  <li>Additionally, the examples I gave in my discussion of the naïve approach are all worst-case scenarios, where an odd-frequency value is only ever encountered at the <em>end</em> of <code class="language-plaintext highlighter-rouge">test_array</code>.  What if it’s the <em>first</em> element?  Isn’t it an awful lot of work populating that <code class="language-plaintext highlighter-rouge">tallies</code> hash unnecessarily?</li>
  <li>
    <p>Additionally additionally, it seems like we could cut down on a bunch of unnecessary iterations of <code class="language-plaintext highlighter-rouge">test_array</code> if we only search a given value <em>once</em>:</p>

    <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">find_method_that_logs_failures</span><span class="p">(</span><span class="n">test_array</span><span class="p">)</span>
  <span class="n">evens</span> <span class="o">=</span> <span class="p">[</span> <span class="p">]</span>

  <span class="n">test_array</span><span class="p">.</span><span class="nf">find</span> <span class="k">do</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span>
    <span class="c1"># Move on if this value has already been checked.</span>
    <span class="k">next</span> <span class="k">if</span> <span class="n">evens</span><span class="p">.</span><span class="nf">include?</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">test_array</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="n">value</span><span class="p">).</span><span class="nf">odd?</span>
      <span class="c1"># Hooray, you found it!  Return true so that #find returns this value.</span>
      <span class="kp">true</span>

    <span class="k">else</span>
      <span class="c1"># Add the value to the `evens` array so we don't check it again.</span>
      <span class="n">evens</span> <span class="o">&lt;&lt;</span> <span class="n">value</span>

      <span class="c1"># Return false so that #find continues working through `test_array`</span>
      <span class="kp">false</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div>    </div>

    <p>Or, here’s a much more elegant implementation I saw:</p>

    <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">find_and_count_unique</span><span class="p">(</span><span class="n">test_array</span><span class="p">)</span>
  <span class="c1"># Reducing `test_array` to only its unique values with `#uniq` *also*</span>
  <span class="c1"># avoids redundant searches.</span>
  <span class="n">test_array</span><span class="p">.</span><span class="nf">uniq</span><span class="p">.</span><span class="nf">find</span> <span class="p">{</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span> <span class="n">test_array</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="n">value</span><span class="p">).</span><span class="nf">odd?</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre></div>    </div>
  </li>
</ul>

<h2 id="benchmark-bumbum">Benchmark Bumbum</h2>
<p><a href="https://github.com/ruby/benchmark">Ruby’s Benchmark module</a> has a pretty easy interface to work with, including its <code class="language-plaintext highlighter-rouge">Benchmark#bmbm</code> method that lets you run the benchmarks multiple times, throwing away initial results<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">4</a></sup> and averaging the rest.  I started by creating my testing array:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">example_array</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">20</span><span class="p">).</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">number</span><span class="o">|</span>
  <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">1_000_000</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="k">end</span><span class="p">.</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span><span class="nb">rand</span><span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">20</span><span class="p">)).</span><span class="nf">shuffle</span>
</code></pre></div></div>
<p>This builds me a shuffled array of numbers between <code class="language-plaintext highlighter-rouge">1</code> and <code class="language-plaintext highlighter-rouge">20</code>, a million of each, plus one more of one of those numbers to ensure <em>something</em> is oddly-frequent.  It’s a big array.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">puts</span> <span class="n">example_array</span><span class="p">.</span><span class="nf">length</span> <span class="c1">#=&gt; 20_000_001</span>
</code></pre></div></div>

<p>This represents a general case where the oddly-frequent value could be anywhere.  I ran this benchmark against the naïvest <code class="language-plaintext highlighter-rouge">#find_and_count</code>, the improved <code class="language-plaintext highlighter-rouge">#find_and_count_unique</code>, and my <code class="language-plaintext highlighter-rouge">#tally_and_check</code>, with the following results:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'benchmark'</span>

<span class="no">Benchmark</span><span class="p">.</span><span class="nf">bmbm</span> <span class="k">do</span> <span class="o">|</span><span class="n">approach</span><span class="o">|</span>
  <span class="n">approach</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'Find and Count'</span><span class="p">)</span> <span class="p">{</span> <span class="n">find_and_count</span><span class="p">(</span><span class="n">example_array</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">approach</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'Find and Count Unique'</span><span class="p">)</span> <span class="p">{</span> <span class="n">find_and_count_unique</span><span class="p">(</span><span class="n">example_array</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">approach</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'Tally and Check'</span><span class="p">)</span> <span class="p">{</span> <span class="n">tally_and_check</span><span class="p">(</span><span class="n">example_array</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>                            user     system      total        real
Find and Count          0.660249   0.003853   0.664102 (  0.667515)
Find and Count Unique   1.348574   0.148201   1.496775 (  1.506050)
Tally and Check         2.183379   0.000000   2.183379 (  2.193576)
</code></pre></div></div>

<h3 id="i-knew-it">I knew it!</h3>

<p>Gratifyingly, this confirmed all my fears that I could still be wrong, and therefore all was right with the world.  Multiple runs tended to produce similar results, though the margin by which the <code class="language-plaintext highlighter-rouge">#find_and_count</code> approaches beat my <code class="language-plaintext highlighter-rouge">#tally_and_check</code> varied pretty wildly.  Raising this with Tim, we agreed that the variation might have to do with encountering the oddly-frequent value earlier or later in the process (ie., closer to a best-case or worst-case scenario).  He suggested testing those best- and worst-case scenarios specifically:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">best_case_example_array</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">20</span><span class="p">).</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">number</span><span class="o">|</span>
  <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">1_000_000</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="k">end</span><span class="p">.</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">prepend</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>

<span class="n">worst_case_example_array</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">20</span><span class="p">).</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">number</span><span class="o">|</span>
  <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">1_000_000</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="k">end</span><span class="p">.</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span>
</code></pre></div></div>

<p>Both arrays remain twenty million elements long; the best-case scenario is that the first value tested yields the hit, and the worst-case scenario is that the winning value is encountered as late as possible in the array (at address <code class="language-plaintext highlighter-rouge">[18_999_999]</code>, here).</p>

<h3 id="best-case-scenario">Best-Case Scenario</h3>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Benchmark</span><span class="p">.</span><span class="nf">bmbm</span> <span class="k">do</span> <span class="o">|</span><span class="n">approach</span><span class="o">|</span>
  <span class="n">approach</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'Find and Count'</span><span class="p">)</span> <span class="p">{</span> <span class="n">find_and_count</span><span class="p">(</span><span class="n">best_case_example_array</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">approach</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'Find and Count Unique'</span><span class="p">)</span> <span class="p">{</span> <span class="n">find_and_count_unique</span><span class="p">(</span><span class="n">best_case_example_array</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">approach</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'Tally and Check'</span><span class="p">)</span> <span class="p">{</span> <span class="n">tally_and_check</span><span class="p">(</span><span class="n">best_case_example_array</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>                            user     system      total        real
Find and Count          1.142833   0.000000   1.142833 (  1.146753)
Find and Count Unique   1.398049   0.140335   1.538384 (  1.542506)
Tally and Check         1.995043   0.000000   1.995043 (  2.000064)
</code></pre></div></div>

<p>This confirmed the pattern, with little variation.  My guess would be that the extra time for <code class="language-plaintext highlighter-rouge">#find_and_count_unique</code> accounts for needing to iterate over the array an extra time and throw out the duplicate values.</p>

<p>On to the worst-case scenario.</p>

<h3 id="worst-case-scenario">Worst-Case Scenario</h3>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Benchmark</span><span class="p">.</span><span class="nf">bmbm</span> <span class="k">do</span> <span class="o">|</span><span class="n">approach</span><span class="o">|</span>
  <span class="n">approach</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'Find and Count'</span><span class="p">)</span> <span class="p">{</span> <span class="n">find_and_count</span><span class="p">(</span><span class="n">worst_case_example_array</span><span class="p">)</span> <span class="p">}</span>
  <span class="c1"># ...</span>
<span class="k">end</span>
</code></pre></div></div>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>                            user     system      total        real
Find and Count

[Done] exited with code=null in 8160.226 seconds
</code></pre></div></div>

<p>This sure did not finish.  The streetlights dimmed, the fans spun, some <a href="https://en.wikipedia.org/wiki/Magic_smoke">magic smoke</a> leaked out, and more than two hours passed; while it was amusing to watch my CPU bounce the task from core to core like a hot potato, I eventually called it: <code class="language-plaintext highlighter-rouge">#find_and_count</code> could not deal with the worst-case scenario on my machine for an array of this size.  I tried the rest:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Benchmark</span><span class="p">.</span><span class="nf">bmbm</span> <span class="k">do</span> <span class="o">|</span><span class="n">approach</span><span class="o">|</span>
  <span class="c1"># approach.report('Find and Count') { find_and_count(worst_case_example_array) }</span>
  <span class="n">approach</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'Find and Count Unique'</span><span class="p">)</span> <span class="p">{</span> <span class="n">find_and_count_unique</span><span class="p">(</span><span class="n">worst_case_example_array</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">approach</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'Tally and Check'</span><span class="p">)</span> <span class="p">{</span> <span class="n">tally_and_check</span><span class="p">(</span><span class="n">worst_case_example_array</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>                            user     system      total        real
Find and Count Unique   2.166757   0.131237   2.297994 (  2.304235)
Tally and Check         1.771231   0.000009   1.771240 (  1.775164)
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">#tally_and_check</code> remains steady, as expected, while the optimized <code class="language-plaintext highlighter-rouge">#find_and_count_unique</code>’s run of two seconds and change is substantially better than <code class="language-plaintext highlighter-rouge">#find_and_count</code>’s never-ever.  Turns out it’s faster to iterate over a twenty million-element array twenty times than it is to iterate over it twenty <em>million</em> times.  Thanks, computer science!</p>

<h2 id="moar-numbers">Moar Numbers</h2>

<p>Tim also pointed out that extending the possible range of values the array could hold might be more realistic and illustrative of the benefits of <code class="language-plaintext highlighter-rouge">#tally_and_check</code>, so I introduced a new version of each of the existing example arrays:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">tims_example_array</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">2000</span><span class="p">).</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">number</span><span class="o">|</span>
  <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">10_000</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="k">end</span><span class="p">.</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span><span class="nb">rand</span><span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">2000</span><span class="p">)).</span><span class="nf">shuffle</span>

<span class="n">tims_best_case_example_array</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">2000</span><span class="p">).</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">number</span><span class="o">|</span>
  <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">10_000</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="k">end</span><span class="p">.</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">prepend</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>

<span class="n">tims_worst_case_example_array</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">2000</span><span class="p">).</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">number</span><span class="o">|</span>
  <span class="no">Array</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">10_000</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="k">end</span><span class="p">.</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span><span class="mi">2000</span><span class="p">)</span>
</code></pre></div></div>

<p>These arrays remain twenty million elements long but now have an even distribution of <em>two thousand</em> values, rather than <em>twenty</em>.  My approach for describing best- and worst-case scenarios also remains consistent.</p>

<h3 id="best-case-scenario-again">Best-Case Scenario Again</h3>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Benchmark</span><span class="p">.</span><span class="nf">bmbm</span> <span class="k">do</span> <span class="o">|</span><span class="n">approach</span><span class="o">|</span>
  <span class="n">approach</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'Find and Count'</span><span class="p">)</span> <span class="p">{</span> <span class="n">find_and_count</span><span class="p">(</span><span class="n">tims_best_case_example_array</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">approach</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'Find and Count Unique'</span><span class="p">)</span> <span class="p">{</span> <span class="n">find_and_count_unique</span><span class="p">(</span><span class="n">tims_best_case_example_array</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">approach</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'Tally and Check'</span><span class="p">)</span> <span class="p">{</span> <span class="n">tally_and_check</span><span class="p">(</span><span class="n">tims_best_case_example_array</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>                            user     system      total        real
Find and Count          0.105408   0.000002   0.105410 (  0.105981)
Find and Count Unique   0.652062   0.134653   0.786715 (  0.793576)
Tally and Check         2.552200   0.000000   2.552200 (  2.573192)
</code></pre></div></div>

<p>I see the same pattern here that I did in the previous best-case scenario, just more extreme.  My guess is that, rather than needing to count <em>a million and one</em> <code class="language-plaintext highlighter-rouge">1</code>s before being sure we’d found the oddly-frequent value, the search only needed to get to <em>ten thousand and one</em>.  … Either that, or the previous worst-case scenario fused my CPU into an unexpectedly superior configuration somehow.  Probably not.</p>

<p>But, speaking of worst-case scenarios…</p>

<h3 id="worst-case-scenario-again">Worst-Case Scenario Again</h3>

<p>I’m not bothering to try this on <code class="language-plaintext highlighter-rouge">#find_and_count</code> again.  I can only afford so much magic smoke.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Benchmark</span><span class="p">.</span><span class="nf">bmbm</span> <span class="k">do</span> <span class="o">|</span><span class="n">approach</span><span class="o">|</span>
  <span class="c1"># approach.report('Find and Count') { find_and_count(tims_worst_case_example_array) }  # NOPE.</span>
  <span class="n">approach</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'Find and Count Unique'</span><span class="p">)</span> <span class="p">{</span> <span class="n">find_and_count_unique</span><span class="p">(</span><span class="n">tims_worst_case_example_array</span><span class="p">)</span> <span class="p">}</span>
  <span class="n">approach</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s1">'Tally and Check'</span><span class="p">)</span> <span class="p">{</span> <span class="n">tally_and_check</span><span class="p">(</span><span class="n">tims_worst_case_example_array</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>                            user     system      total        real
Find and Count Unique 174.092475   0.138251 174.230726 (174.687193)
Tally and Check         1.859082   0.000001   1.859083 (  1.863179)
</code></pre></div></div>

<p>There we go: three minutes versus <code class="language-plaintext highlighter-rouge">#tally_and_check</code>’s consistent two seconds.  It makes sense to me that, as the number of possible values approaches the overall length of the array, the benefit of compressing the array using <code class="language-plaintext highlighter-rouge">#uniq</code> to avoid redundant searches approaches zero (and then becomes a liability in the end); the slowness of <code class="language-plaintext highlighter-rouge">#find_and_count_unique</code> approaches the slowness of the naïviëst <code class="language-plaintext highlighter-rouge">#find_and_count</code> and melts my computer.  Neat.</p>

<h2 id="fine-then">Fine, then.</h2>

<p>After yet another bracing foray into the realm of computer-processed pointlessness, I’m prepared to admit that maybe I might be right in suspecting that <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span> is faster than <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n^2)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0641em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>.  Thanks again for the fun, Tim.</p>

<p class="read-more">… Of course, <a href="/blog/2023-03-07-benchmarking-an-old-chestnut-dead-horse-edition/" class="heading flip-title">there’s a better way</a></p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:2" role="doc-endnote">
      <p>Imagine if we needed to make an API request each time, geez. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:1" role="doc-endnote">
      <p>That is, the solution I forgot I remembered. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>Tim points out that accessing the value at a given location in either an array/list or a hash/dictionary takes “constant time” (or <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span> in big O notation) no matter how large the structure.  In this case, though, since we don’t yet know which key holds an odd value, we still need to iterate over <code class="language-plaintext highlighter-rouge">tallies</code> up to once, which remains an <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span> operation.  Those two iterations in <code class="language-plaintext highlighter-rouge">#tally_and_check</code> amount to <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>2</mn><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(2n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord">2</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span> in this particular case, but it seems to be conventional to drop the coefficient. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>Handy if it takes your machine a little while to throttle up (or throttle out) <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Danny Fekete</name><email>danny@neckdeep.dev</email></author><category term="blog" /><category term="programming, math, optimization" /><summary type="html"><![CDATA[Testing linear vs. quadratic time-complexity to reluctantly prove myself not wrong.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://neckdeep.dev/assets/blog/img/rodin-chestnut.png" /><media:content medium="image" url="https://neckdeep.dev/assets/blog/img/rodin-chestnut.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">A Simple Sandwich, Contorno</title><link href="https://neckdeep.dev/blog/2023-01-09-a-simple-sandwich-contorno/" rel="alternate" type="text/html" title="A Simple Sandwich, Contorno" /><published>2023-01-09T00:00:00+00:00</published><updated>2023-01-13T17:41:42+00:00</updated><id>https://neckdeep.dev/blog/a-simple-sandwich-contorno</id><content type="html" xml:base="https://neckdeep.dev/blog/2023-01-09-a-simple-sandwich-contorno/"><![CDATA[<blockquote class="note smaller" title="Continuity Alert">
  <p>This post is the third course (really more of a <a href="https://en.wikipedia.org/wiki/Italian_meal_structure#Contorno">side-dish</a>) in a series of silly articles where I use Ruby to build sandwiches.  It won’t make much sense if you haven’t read <a href="/blog/2022-12-13-a-simple-sandwich-i/" class="heading flip-title">Part I</a> and <a href="/blog/2022-12-19-a-simple-sandwich-ii/" class="heading flip-title">Part II</a> first.  Good luck!</p>

  <p>Also, my updated gist is available <a href="https://gist.github.com/StandardGiraffe/b946bc787def30807fe2f68de846d0c3/164fa2a5369db743fcc7ca283f9cb5e611133257">here</a> if you want to skip straight to dessert.</p>
</blockquote>

<p>At the time of writing, I know how to program reasonably well in about 2.001 languages<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.  I’ve found that using external resources for learning new languages or advancing existing proficiency usually requires finding an author I can sufficiently relate to: I need someone who describes concepts with examples familiar enough that I can map them onto my existing knowledge, because then I can see how what I <em>already</em> know is similar or different.  Building that sense of contrast and recognition gradually furnishes a clunky, working familiarity that I ideally build into fluency with practice and subsequent exploration as my confidence grows.  At the outset, then, it’s critical for me that I can form that initial bridge; <em>examples that make sense help me learn</em> — surprise.</p>

<p>So, I’m currently enjoying a weird opportunity: my friend and former Lighthouse Labs mentor, <a href="https://timjohns.ca">Tim Johns</a>, was kind enough to read my <a href="/blog/2022-12-13-a-simple-sandwich-i/">silly Sandwich article</a> and got to thinking about how <em>he</em> would have approached the same problem, ultimately writing <a href="https://timjohns.ca/a-simple-sandwich-part-i.html">his own response article</a>.  Besides serving as a bonus code review by a deeply respected colleague (and flattering as heck), Tim’s current language of choice is <a href="https://en.wikipedia.org/wiki/Haskell">Haskell</a> and programming paradigm is <a href="https://en.wikipedia.org/wiki/Functional_programming">functional</a> — these are far removed from my main experience as an <a href="https://en.wikipedia.org/wiki/Object-oriented_programming">object-oriented</a> <a href="https://en.wikipedia.org/wiki/Ruby_(programming_language)">Ruby</a> programmer, so they already present a juicy learning opportunity.  What makes it a windfall is that Tim is working from <em>my own examples</em>, first replicating (transliterating?) my code into working but <em>non-ideomatic</em> Haskell<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>, and then re-writing it without the Ruby-accent, free to showcase Haskell’s paradigmatic and philosophical affordances.  It’s like he came to the island of Dannyland, tidied up the coastline, built a bridge to Timtopia that lands at faithfully engineered “Little Dannyland” welcome village, and then implemented conveyor belts on the bridge wherever it started to get steep.  I believe his intention in all this was just to have fun, but the result suggests to me that he can’t help being a mentor whenever he does <em>anything</em>.</p>

<ul id="markdown-toc">
  <li><a href="#purity" id="markdown-toc-purity">Purity</a>    <ul>
      <li><a href="#tidying-up-my-sloppy-joe" id="markdown-toc-tidying-up-my-sloppy-joe">Tidying up My Sloppy Joe</a>        <ul>
          <li><a href="#lobotomizing-my-bread" id="markdown-toc-lobotomizing-my-bread">Lobotomizing my Bread</a></li>
          <li><a href="#zip-it-up" id="markdown-toc-zip-it-up">Zip It Up!</a></li>
          <li><a href="#and-it-was-good" id="markdown-toc-and-it-was-good">And It Was Good</a></li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h2 id="purity">Purity</h2>

<p>In our conversations and as a side-effect<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">3</a></sup> of his article, I’m developing a greater attentiveness to places where I can keep my functions “<a href="https://en.wikipedia.org/wiki/Pure_function">pure</a>” — meaning, where possible, my functions should return identical results when fed identical inputs (ignoring state) and avoid changing state themselves.  <code class="language-plaintext highlighter-rouge">Chef#make_me_a_sandwich!</code>’s sandwich-assembly process seemed like a good place to attempt a refactor.  As a reminder:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Chef</span>
  <span class="c1"># ...</span>

  <span class="k">def</span> <span class="nf">make_me_a_sandwich!</span><span class="p">(</span><span class="o">*</span><span class="n">requested_flavours</span><span class="p">)</span>
    <span class="c1"># ...</span>

    <span class="n">ensure_condiments_are_actually_condiments</span>
    <span class="n">ensure_bread_is_actually_bread</span>
    <span class="n">ensure_knife</span>

    <span class="n">ensure_condiments_are_available</span><span class="p">(</span><span class="n">requested_flavours</span><span class="p">)</span>
    <span class="n">ensure_enough_bread_is_available</span><span class="p">(</span><span class="n">requested_flavours</span><span class="p">)</span>

    <span class="n">sandwich</span> <span class="o">=</span> <span class="no">Sandwich</span><span class="p">.</span><span class="nf">new</span>

    <span class="vi">@knife</span><span class="p">.</span><span class="nf">clean!</span>

    <span class="n">surface_to_smear</span> <span class="o">=</span> <span class="ss">:top</span>
    <span class="n">current_slice</span> <span class="o">=</span> <span class="vi">@slices_of_bread</span><span class="p">.</span><span class="nf">pop</span>

    <span class="n">requested_flavours</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">flavour</span><span class="o">|</span>
      <span class="n">condiment</span> <span class="o">=</span> <span class="vi">@condiments</span><span class="p">.</span><span class="nf">find</span> <span class="p">{</span> <span class="o">|</span><span class="n">jar</span><span class="o">|</span> <span class="n">jar</span><span class="p">.</span><span class="nf">contents</span> <span class="o">==</span> <span class="n">flavour</span><span class="p">}</span>
      <span class="n">condiment</span><span class="p">.</span><span class="nf">open!</span>

      <span class="vi">@knife</span><span class="p">.</span><span class="nf">load_from!</span><span class="p">(</span><span class="n">condiment</span><span class="p">)</span>

      <span class="n">current_slice</span><span class="p">.</span><span class="nf">smear!</span><span class="p">(</span>
        <span class="ss">knife: </span><span class="vi">@knife</span><span class="p">,</span>
        <span class="ss">surface: </span><span class="n">surface_to_smear</span>
      <span class="p">)</span>

      <span class="c1"># If ready, add the current slice to the stack and grab another.</span>
      <span class="k">if</span> <span class="n">current_slice</span><span class="p">.</span><span class="nf">top</span><span class="p">.</span><span class="nf">smeared?</span>
        <span class="n">sandwich</span> <span class="o">&lt;&lt;</span> <span class="n">current_slice</span>
        <span class="n">current_slice</span> <span class="o">=</span> <span class="vi">@slices_of_bread</span><span class="p">.</span><span class="nf">pop</span>
      <span class="k">end</span>

      <span class="c1"># Swap the target surface for the next condiment.</span>
      <span class="n">surface_to_smear</span> <span class="o">=</span> <span class="p">(</span><span class="n">surface_to_smear</span> <span class="o">==</span> <span class="ss">:top</span> <span class="p">?</span> <span class="ss">:bottom</span> <span class="p">:</span> <span class="ss">:top</span><span class="p">)</span>
    <span class="k">end</span>

    <span class="c1"># Add the final slice</span>
    <span class="n">sandwich</span> <span class="o">&lt;&lt;</span> <span class="n">current_slice</span>

    <span class="c1"># The magic happens</span>
    <span class="n">sandwich</span><span class="p">.</span><span class="nf">build!</span>
    <span class="n">sandwich</span><span class="p">.</span><span class="nf">cut!</span><span class="p">(</span><span class="vi">@knife</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">sandwich</span><span class="p">.</span><span class="nf">ready_to_eat?</span>  <span class="c1"># How could it not be? Still.</span>
      <span class="nb">puts</span> <span class="s2">"Finit.  One </span><span class="si">#{</span><span class="n">sandwich</span><span class="p">.</span><span class="nf">flavours_human_readable</span><span class="si">}</span><span class="s2"> sandwich; bon appétit!"</span>

      <span class="k">return</span> <span class="n">sandwich</span>  <span class="c1"># Needlessly explicit "return"; assume to be flourish.</span>
    <span class="k">else</span>
      <span class="nb">puts</span> <span class="s2">"Hmm.  Something went wrong despite my genius."</span>
    <span class="k">end</span>
  <span class="k">end</span>
</code></pre></div></div>

<p>One of the reasons Tim likes functional programming is that it encourages you to break down a problem into its components and try to solve them independently]<sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote" rel="footnote">4</a></sup>.  Looking at this function as I’ve written it, with all its reliance on mutating local variables (like my <code class="language-plaintext highlighter-rouge">surface_to_smear</code> flippable bit) and instance properties (like the contents of <code class="language-plaintext highlighter-rouge">@slices_of_bread</code> as I <code class="language-plaintext highlighter-rouge">pop</code> slices off the end of it), I can’t help but feel self-conscious.  It’s bloated, doing all kinds of things, and managing all kinds of responsibilities.  It is egregiously <a href="https://timjohns.ca/imperative-vs-declarative-with-pumpkin-pie.html">imperative rather than declarative</a>, so the order of its operations is set manually by me (and isn’t particularly resilient to change or shuffling).  Even I can see we’re moving away from inherent limitations of OOP and into Plain-Old-Sloppy territory.</p>

<h3 id="tidying-up-my-sloppy-joe">Tidying up My Sloppy Joe</h3>

<p>Let me see if I can break this problem down.  I need to return a valid <code class="language-plaintext highlighter-rouge">sandwich</code> containing <em>n</em> <code class="language-plaintext highlighter-rouge">requested_flavours</code>.  That means:</p>

<blockquote class="note smaller" title="Rules of Sandwichness">
  <ul>
    <li>Bottom and top slices have plain bottom and top surfaces respectively</li>
    <li>Inner surfaces need to be smeared, with additional slices of bread added if enough surfaces are unavailable.
      <ul>
        <li>Both surfaces of internal slices of bread are valid condiment recipients.</li>
      </ul>
    </li>
    <li>A sandwich can contain no plain slices of bread, excepting the top slice.</li>
    <li>Sandwich must be <code class="language-plaintext highlighter-rouge">ready_to_eat</code>, being both <code class="language-plaintext highlighter-rouge">build!</code>ed<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup> and <code class="language-plaintext highlighter-rouge">cut!</code>.</li>
  </ul>
</blockquote>

<p>The trickiest part of making this sandwich <em>seems</em> to involve getting the bread surfaces smeared in the right order.  My original solution involved searching for the right condiment and applying it to the bread in one jumbled swoop, flipping between the top and bottom of each slice while I iterated through my list of user-requested flavours; I think I can start breaking this problem down by dealing with the condiments and the bread separately.</p>

<p>For the condiments, I’ll abstract the search logic out to a private function that incorporates my <code class="language-plaintext highlighter-rouge">ensure_condiments...</code> safety checks and returns an array of ready, open jars.  Because of those safety functions, I’m guaranteed that any time the method is called with a given array of flavours, it will always return the same array of jars.<sup id="fnref:8" role="doc-noteref"><a href="#fn:8" class="footnote" rel="footnote">6</a></sup></p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Chef</span>
  <span class="c1"># ...</span>
<span class="kp">private</span>
  <span class="k">def</span> <span class="nf">opened_condiment_jars_for</span><span class="p">(</span><span class="n">requested_flavours</span><span class="p">)</span>
    <span class="c1"># Insurance:</span>
    <span class="n">ensure_condiments_are_actually_condiments</span>
    <span class="n">ensure_condiments_are_available</span><span class="p">(</span><span class="n">requested_flavours</span><span class="p">)</span>

    <span class="c1"># Array Building:</span>
    <span class="n">requested_flavours</span><span class="p">.</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">flavour</span><span class="o">|</span>
      <span class="n">condiment</span> <span class="o">=</span> <span class="vi">@condiments</span><span class="p">.</span><span class="nf">find</span> <span class="p">{</span> <span class="o">|</span><span class="n">jar</span><span class="o">|</span> <span class="n">jar</span><span class="p">.</span><span class="nf">contents</span> <span class="o">==</span> <span class="n">flavour</span><span class="p">}</span>
      <span class="n">condiment</span><span class="p">.</span><span class="nf">open!</span>

      <span class="n">condiment</span>
    <span class="k">end</span>
  <span class="k">end</span>
</code></pre></div></div>

<p>Regarding the bread, <a href="/blog/2022-12-19-a-simple-sandwich-ii/#bread-get">I’ve already figured out</a> how to know the total number of slices I’ll need for any given sandwich ahead of time with the code:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sufficient_slices</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="p">(</span><span class="n">requested_flavours</span><span class="p">.</span><span class="nf">count</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">).</span><span class="nf">ceil</span>
</code></pre></div></div>

<p>I’ll formalize that knowledge in <code class="language-plaintext highlighter-rouge">Chef</code> as a class method (representing the idea that to be a <code class="language-plaintext highlighter-rouge">Chef</code> is to know how many slices are needed for a given sandwich), and then, as with <code class="language-plaintext highlighter-rouge">#opened_condiment_jars_for</code>, I’ll just abstract the bread-collection process out to a private method and pop in the safety checks:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Chef</span>
  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">number_of_slices_for</span><span class="p">(</span><span class="n">number_of_requested_flavours</span><span class="p">)</span>
    <span class="mi">1</span> <span class="o">+</span> <span class="p">(</span><span class="n">number_of_requested_flavours</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">).</span><span class="nf">ceil</span>
  <span class="k">end</span>

  <span class="c1"># ...</span>
<span class="kp">private</span>
  <span class="c1"># ...</span>

  <span class="k">def</span> <span class="nf">plain_slices_for</span><span class="p">(</span><span class="n">number_of_requested_flavours</span><span class="p">)</span>
    <span class="n">ensure_bread_is_actually_bread</span>
    <span class="n">ensure_enough_bread_is_available</span><span class="p">(</span><span class="n">number_of_requested_flavours</span><span class="p">)</span>

    <span class="c1"># #slice! will remove the specified range of elements from the host array</span>
    <span class="c1"># and return them as a new array.  The "- 1" accounts for Ruby arrays</span>
    <span class="c1"># being zero-indexed.</span>
    <span class="vi">@slices_of_bread</span><span class="p">.</span><span class="nf">slice!</span><span class="p">(</span>
      <span class="mi">0</span><span class="o">..</span><span class="p">(</span><span class="no">Chef</span><span class="p">.</span><span class="nf">number_of_slices_for</span><span class="p">(</span><span class="n">number_of_requested_flavours</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
    <span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Finally, just so I know I’m dealing with a clean, ready knife, I’ll add one more private method to take care of that.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Chef</span>
  <span class="c1"># ...</span>
<span class="kp">private</span>
  <span class="c1"># ...</span>

  <span class="k">def</span> <span class="nf">readied_knife</span>
    <span class="n">ensure_knife</span>

    <span class="vi">@knife</span><span class="p">.</span><span class="nf">clean!</span>

    <span class="vi">@knife</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Now, at the start of the sandwich-assembling process, I’ll run all three of my new methods and wind up with my ready and usable condiments and sandwich bread as two arrays, plus a reliable smearin’ knife:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">make_me_a_sandwich!</span><span class="p">(</span><span class="o">*</span><span class="n">requested_flavours</span><span class="p">)</span>
  <span class="c1"># Courtesy, etc.</span>

  <span class="n">condiments</span> <span class="o">=</span> <span class="n">opened_condiment_jars_for</span><span class="p">(</span><span class="n">requested_flavours</span><span class="p">)</span>
  <span class="n">sandwich_bread</span> <span class="o">=</span> <span class="n">plain_slices_for</span><span class="p">(</span><span class="n">requested_flavours</span><span class="p">.</span><span class="nf">count</span><span class="p">)</span>
  <span class="n">knife</span> <span class="o">=</span> <span class="n">readied_knife</span>

  <span class="c1"># ...</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Incidentally, my insurance methods are now all covered in the various prep tasks I’ve just created, so I no longer need the checklist section at the start of <code class="language-plaintext highlighter-rouge">#make_me_a_sandwich!</code>.  This is starting to feel more declarative and less micro-managed.</p>

<p>Another reason I like this approach is that I wind up with a correctly-sized array of sliced bread that I can <code class="language-plaintext highlighter-rouge">map!</code> over: the shape of that array is the same as the shape (and medium) of my final sandwich, whereas the list of requested flavours was not.  So, hopefully, I can do something like the following code, and be triumphant:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sandwich_bread</span><span class="p">.</span><span class="nf">map!</span> <span class="k">do</span>
  <span class="c1"># something clever</span>
<span class="k">end</span>  <span class="c1">#=&gt; smeared_and_sandwichable_bread</span>
</code></pre></div></div>

<blockquote class="note smaller" title="Mea cuplpa:">
  <p>I admit it: I <em>am</em> changing state with my <code class="language-plaintext highlighter-rouge">Array#slice!</code> and <code class="language-plaintext highlighter-rouge">Array#map!</code> methods<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">7</a></sup>.  In the end, I’ve decided that I won’t throw out all of OOP’s benefits while trying to clean up my code with some pure functions — that would be heading down the path of transliterating Haskell into Ruby, and I’d meet Tim coming the other way.</p>
</blockquote>

<p>It seems to me that given the right number of slices of bread, the desired condiments, and the validation rules for a sandwich, I ought to come out with the same sandwich every time.  That sounds like a pure function — I just need to solve the general case for dealing with any given element of the array.</p>

<p>Initially, I’d been trying to figure out a fancy way of determining which side of which slice of bread a given condiment belonged to using even and odd indices of <code class="language-plaintext highlighter-rouge">condiments</code>, but in the end I realized I could just explode my bread array into its surfaces and interleave them with the condiments.  <code class="language-plaintext highlighter-rouge">Array#zip</code> in Ruby is great for this: it allows me to iterate through multiple arrays at once; normally it returns a new array of matched elements, but I can also give it a block and perform an action at each step:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Basic Usage:</span>
<span class="p">[</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span> <span class="p">].</span><span class="nf">zip</span><span class="p">([</span><span class="s1">'A'</span><span class="p">,</span> <span class="s1">'B'</span><span class="p">,</span> <span class="s1">'C'</span> <span class="p">])</span>  <span class="c1">#=&gt; [ [ 1, 'A' ], [ 2, 'B' ], [ 3, 'C' ] ]</span>

<span class="c1"># Fun Usage:</span>
<span class="p">[</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span> <span class="p">].</span><span class="nf">zip</span> <span class="p">[</span><span class="s1">'A'</span><span class="p">,</span> <span class="s1">'B'</span><span class="p">,</span> <span class="s1">'C'</span> <span class="p">]</span> <span class="k">do</span> <span class="o">|</span> <span class="n">number</span><span class="p">,</span> <span class="n">letter</span> <span class="o">|</span>
  <span class="nb">puts</span> <span class="s2">"</span><span class="si">#{</span><span class="n">number</span><span class="si">}</span><span class="s2"> and </span><span class="si">#{</span><span class="n">letter</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>  <span class="c1">#=&gt;</span>
</code></pre></div></div>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1 and A
2 and B
3 and C
</code></pre></div></div>

<p>If I can <code class="language-plaintext highlighter-rouge">zip</code> the correct surfaces up to the correct condiments, it’s just a matter of smearing the former with the latter.  I’ll skip the first surface to keep the bottom of the sandwich unsmeared (<code class="language-plaintext highlighter-rouge">sandwich_bread_surfaces[1..]</code> in Ruby syntax), and then proceed safe in the knowledge that, because I started with the correct amount of bread, I won’t need to worry about running out or the top of my sandwich getting icky.</p>

<p>… At this point, I run into the fact that I’ve only taught <code class="language-plaintext highlighter-rouge">Bread</code> how to be smeared, not <code class="language-plaintext highlighter-rouge">Bread::Surface</code>s.  Damn.  And, in retrospect, it makes more sense that a <code class="language-plaintext highlighter-rouge">Knife</code> should to know how to <code class="language-plaintext highlighter-rouge">#smear</code> something, rather than <code class="language-plaintext highlighter-rouge">Bread</code>.  OOPs.  I’ll need to fix all that quickly before I continue.  (If you don’t want to be distracted by the gory details, <a href="#zip-it-up">just skip ahead</a>.)</p>

<h4 id="lobotomizing-my-bread">Lobotomizing my Bread</h4>

<p>Alright, damn it.  A band-aid (it seems to me) would be to have <code class="language-plaintext highlighter-rouge">Bread</code> pass <code class="language-plaintext highlighter-rouge">self</code> to any <code class="language-plaintext highlighter-rouge">Surface</code>s it instantiates.  Then, we could ask any given <code class="language-plaintext highlighter-rouge">surface</code> to tell its parent <code class="language-plaintext highlighter-rouge">bread</code> to “get smeared” (<code class="language-plaintext highlighter-rouge">surface.bread.smear!(etc.)</code>).  This feels tortuous and again, it really is the knife that does the smearing.  Here’s how I’ve rejiggered these classes:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Knife</span>
  <span class="k">class</span> <span class="nc">EmptyKnifeError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>
  <span class="k">class</span> <span class="nc">InvalidTargetError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>

  <span class="c1"># ...</span>

  <span class="k">def</span> <span class="nf">smear!</span><span class="p">(</span><span class="n">bread_surface</span><span class="p">)</span>
    <span class="k">unless</span> <span class="n">loaded?</span>
      <span class="k">raise</span> <span class="no">EmptyKnifeError</span><span class="p">,</span> <span class="s2">"This knife is too unbesmirched by condiment to smear anything!  Load it first."</span>
    <span class="k">end</span>

    <span class="k">unless</span> <span class="n">bread_surface</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">SliceOfBread</span><span class="o">::</span><span class="no">Surface</span><span class="p">)</span>
      <span class="k">raise</span> <span class="no">InvalidTargetError</span><span class="p">,</span> <span class="s2">"</span><span class="si">#{</span><span class="n">bread_surface</span><span class="si">}</span><span class="s2"> isn't the surface of a slice of bread!  Put the knife down!"</span>
    <span class="k">end</span>

    <span class="n">bread_surface</span><span class="p">.</span><span class="nf">be_smeared!</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">class</span> <span class="nc">SliceOfBread::Surface</span>
  <span class="k">class</span> <span class="nc">AlreadySmearedError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>

  <span class="c1"># ...</span>

  <span class="k">def</span> <span class="nf">be_smeared!</span><span class="p">(</span><span class="n">knife</span><span class="p">)</span>
    <span class="k">unless</span> <span class="n">knife</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">Knife</span><span class="p">)</span>
      <span class="k">raise</span> <span class="o">::</span><span class="no">Knife</span><span class="o">::</span><span class="no">InvalidKnifeError</span><span class="p">,</span> <span class="s2">"That's not hygienic."</span>
    <span class="k">end</span>

    <span class="k">unless</span> <span class="n">plain?</span>
      <span class="k">raise</span> <span class="no">AlreadySmearedError</span><span class="p">,</span> <span class="s2">"This surface was already smeared!"</span>
    <span class="k">end</span>

    <span class="k">unless</span> <span class="n">knife</span><span class="p">.</span><span class="nf">loaded?</span>
      <span class="k">raise</span> <span class="o">::</span><span class="no">Knife</span><span class="o">::</span><span class="no">EmptyKnifeError</span><span class="p">,</span> <span class="s2">"This knife is still too clean to smear with.  How did this even happen...?"</span>
    <span class="k">end</span>

    <span class="nb">self</span><span class="p">.</span><span class="nf">contents</span> <span class="o">=</span> <span class="n">knife</span><span class="p">.</span><span class="nf">contents</span>
    <span class="n">knife</span><span class="p">.</span><span class="nf">contents</span> <span class="o">=</span> <span class="kp">nil</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This leaves my <code class="language-plaintext highlighter-rouge">SliceOfBread</code> class a lot simpler: it’s a container for <code class="language-plaintext highlighter-rouge">Surface</code>s and a tool for reporting on them.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">SliceOfBread</span>
  <span class="nb">attr_reader</span> <span class="ss">:top</span>
  <span class="nb">attr_reader</span> <span class="ss">:bottom</span>

  <span class="k">def</span> <span class="nf">initialize</span>
    <span class="vi">@top</span> <span class="o">=</span> <span class="no">Surface</span><span class="p">.</span><span class="nf">new</span>
    <span class="vi">@bottom</span> <span class="o">=</span> <span class="no">Surface</span><span class="p">.</span><span class="nf">new</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">plain?</span>
    <span class="n">top</span><span class="p">.</span><span class="nf">plain?</span> <span class="n">and</span> <span class="n">bottom</span><span class="p">.</span><span class="nf">plain?</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">smeared?</span>
    <span class="n">top</span><span class="p">.</span><span class="nf">smeared?</span> <span class="n">or</span> <span class="n">bottom</span><span class="p">.</span><span class="nf">smeared?</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h4 id="zip-it-up">Zip It Up!</h4>

<p>Okay, geez.  Finally.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Chef</span>
  <span class="k">def</span> <span class="nf">make_me_a_sandwich!</span>
    <span class="c1"># Courtesy ...</span>
    <span class="c1"># ... Tool Preparation ...</span>

    <span class="c1"># Explode my bread into surfaces:</span>
    <span class="n">sandwich_bread_surfaces</span> <span class="o">=</span> <span class="n">sandwich_bread</span><span class="p">.</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">slice</span><span class="o">|</span>
      <span class="p">[</span> <span class="n">slice</span><span class="p">.</span><span class="nf">bottom</span><span class="p">,</span> <span class="n">slice</span><span class="p">.</span><span class="nf">top</span> <span class="p">]</span>
    <span class="k">end</span><span class="p">.</span><span class="nf">flatten</span>

    <span class="c1"># Skipping the bottom surface, apply condiments until we're out of condiments!</span>
    <span class="n">sandwich_bread_surfaces</span><span class="p">[</span><span class="mi">1</span><span class="o">..</span><span class="p">].</span><span class="nf">zip</span><span class="p">(</span><span class="n">condiments</span><span class="p">).</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span> <span class="n">surface</span><span class="p">,</span> <span class="n">condiment</span> <span class="o">|</span>
      <span class="k">break</span> <span class="k">if</span> <span class="o">!</span><span class="n">condiment</span>

      <span class="n">knife</span><span class="p">.</span><span class="nf">load_from!</span><span class="p">(</span><span class="n">condiment</span><span class="p">)</span>
      <span class="n">knife</span><span class="p">.</span><span class="nf">smear!</span><span class="p">(</span><span class="n">surface</span><span class="p">)</span>
    <span class="k">end</span>

    <span class="c1"># ...</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h4 id="and-it-was-good">And It Was Good</h4>

<p>At this point, I still have my <code class="language-plaintext highlighter-rouge">sandwich_bread</code> array, but now it’s all smeared up good.  Let’s <em>deem it a sandwich</em> and go home!</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Chef</span>
  <span class="k">def</span> <span class="nf">make_me_a_sandwich!</span>
    <span class="c1"># Courtesy ...</span>
    <span class="c1"># ... Tool Preparation ...</span>
    <span class="c1"># ... Smearification ...</span>

    <span class="n">sandwich</span> <span class="o">=</span> <span class="no">Sandwich</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="o">*</span><span class="n">sandwich_bread</span><span class="p">)</span>

    <span class="n">sandwich</span><span class="p">.</span><span class="nf">build!</span>
    <span class="n">sandwich</span><span class="p">.</span><span class="nf">cut!</span><span class="p">(</span><span class="n">knife</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">sandwich</span><span class="p">.</span><span class="nf">ready_to_eat?</span>  <span class="c1"># How could it not be?  Still.</span>
      <span class="nb">puts</span> <span class="s2">"Finit.  One </span><span class="si">#{</span><span class="n">sandwich</span><span class="p">.</span><span class="nf">flavours_human_readable</span><span class="si">}</span><span class="s2"> sandwich; bon appétit!"</span>

      <span class="k">return</span> <span class="n">sandwich</span>  <span class="c1"># Needlessly explicit "return"; assume to be flourish.</span>
    <span class="k">else</span>
      <span class="nb">puts</span> <span class="s2">"Hmm.  Something went wrong despite my genius."</span>
    <span class="k">end</span>
  <span class="k">end</span>
</code></pre></div></div>

<p>So:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">daddy</span> <span class="o">=</span> <span class="no">Chef</span><span class="p">.</span><span class="nf">new</span>

<span class="n">lunch</span> <span class="o">=</span> <span class="n">daddy</span><span class="p">.</span><span class="nf">please</span><span class="p">.</span><span class="nf">make_me_a_sandwich!</span><span class="p">(</span><span class="s2">"Relish"</span><span class="p">,</span> <span class="s2">"Marmite"</span><span class="p">,</span> <span class="s2">"Nutella™"</span><span class="p">,</span> <span class="s2">"Sriracha"</span><span class="p">)</span>
<span class="c1">#=&gt;</span>
</code></pre></div></div>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Looks like we could use a few things from the store...
Bought me some Relish
Bought me some Marmite
Bought me some Nutella™
Bought me some Sriracha
There we go.  That's better.
This isn't enough bread to make your sandwich.  Let me just grab some more.
Yoink!
Yoink!
Yoink!
... There we go.  Pile o' bread!
I'll probably want a knife for this...
... Full tang mokume gane butter knife.  Perfect.
Finit.  One relish, marmite, nutella™, and sriracha sandwich; bon appétit!
</code></pre></div></div>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">pp</span> <span class="n">lunch</span> <span class="c1">#=&gt;</span>
</code></pre></div></div>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#&lt;Sandwich:0x00007f7142866860
 @built=true,
 @cut=true,
 @slices=
  [#&lt;SliceOfBread:0x00007f7142862210
    @bottom=#&lt;SliceOfBread::Surface:0x00007f7142861cc0 @contents=nil&gt;,
    @top=#&lt;SliceOfBread::Surface:0x00007f7142862148 @contents="Relish"&gt;&gt;,
   #&lt;SliceOfBread:0x00007f71428619f0
    @bottom=#&lt;SliceOfBread::Surface:0x00007f7142861720 @contents="Marmite"&gt;,
    @top=#&lt;SliceOfBread::Surface:0x00007f7142861770 @contents="Nutella™"&gt;&gt;,
   #&lt;SliceOfBread:0x00007f7142861630
    @bottom=#&lt;SliceOfBread::Surface:0x00007f7142861518 @contents="Sriracha"&gt;,
    @top=#&lt;SliceOfBread::Surface:0x00007f7142861608 @contents=nil&gt;&gt;]&gt;
</code></pre></div></div>

<p>Lovely.  It was a lot of work for exactly the same sandwich, but I think it <a href="https://en.wikipedia.org/wiki/Code_smell">smells</a> a bit better and all that exercise helped me work up an appetite.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Mostly Ruby, the barest bit of Python, and I’m counting JavaScript and Typescript together as a solid 0.85, on a good day. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>He <a href="https://timjohns.ca/a-simple-sandwich-part-i.html#haskell-solution---oop-translation">describes this</a> as like translating a poem from Portugese to English, word for word, resulting in parsable grammar and syntax but losing poetic flow (and presumably the original language’s particular affordances and nuances of meaning)<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">8</a></sup>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p><a href="https://knowyourmeme.com/memes/pun-dog">…</a> <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:7" role="doc-endnote">
      <p>Another is that statically-typed functions tend to be self-explanatory: it’s easy to know what a function will return just by looking at the declaration rather than the contents, and you can rest assured that it won’t mutate any of your other data, being “functional” and “pure”.  Conversely, Ruby offers the convention of bang-endings on “dangerous” methods (whatever “dangerous” means to that particular programmer — usually “I might throw an exception” or “I might mutate something”, but ultimately, “buyer beware”, “do your homework”, etc. <a href="#fnref:7" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p>Tim had a reasonable concern about my <a href="/blog/2022-12-13-a-simple-sandwich-i/#sandwichness"><code class="language-plaintext highlighter-rouge">Sandwich#build!</code> method</a>.  When I wrote it, I imagined that the method’s running of checks was akin to a final tidying up of the stack of smeared bread and deeming it fit to be a <em>real sandwich</em>; Tim pointed out that what this really is is a validation pass: a sandwich can be valid or invalid in my sandwich universe, but a sandwich doesn’t really “build” itself.  This should’ve been <code class="language-plaintext highlighter-rouge">Sandwich#validate!</code>. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:8" role="doc-endnote">
      <p>I guess this isn’t strictly <em>pure</em>, since state is modified when missing jars are procured and all jars are opened, but at least it’s idempotent! <a href="#fnref:8" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:6" role="doc-endnote">
      <p>I love that I’m <code class="language-plaintext highlighter-rouge">slice!</code>ing my bread array. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>Considering Tim’s contemplative and heartfelt appreciation of functional programming, I suspect his choice to use an aesthetic domain for his example is personally apropos. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Danny Fekete</name><email>danny@neckdeep.dev</email></author><category term="blog" /><category term="programming" /><summary type="html"><![CDATA[Refactoring for a more declarative, functional lunch with friends...]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://neckdeep.dev/assets/blog/sandwich/img/sandwich-collab.png" /><media:content medium="image" url="https://neckdeep.dev/assets/blog/sandwich/img/sandwich-collab.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">My 2023 Philosophy</title><link href="https://neckdeep.dev/blog/2023-01-01-my-2023-philosophy/" rel="alternate" type="text/html" title="My 2023 Philosophy" /><published>2023-01-01T00:00:00+00:00</published><updated>2023-03-17T18:14:16+00:00</updated><id>https://neckdeep.dev/blog/my-2023-philosophy</id><content type="html" xml:base="https://neckdeep.dev/blog/2023-01-01-my-2023-philosophy/"><![CDATA[<p class="figcaption">DALL·E’s attempt at “the school of athens by raphael including men, women, and people of various ethnicities, using computers”.  Cute.</p>

<p>Every now and then, I’ve been compelled to articulate my personal philosophy for others, usually in the context of teaching, mentoring, or building artifacts for my son.  It’s a handy exercise and is always privately weird to see how I’ve changed or stayed the same since the last time.</p>

<p>As I’ve embarked on <em>Neck-Deep Development</em> with the hope that it’ll help find me employment with compatible people, I wanted to try to express the things most important to me in the most authentic language I can manage; this is ultimately for you, the reader, as well as for me, the fallible but corrigible living document.</p>

<h2 id="why-i-teach">Why I Teach</h2>

<p>When I was younger, my answer to this was more pure and ideological: I hate ignorance and cruelty and needless suffering, and it seemed like teaching was a way to fix those things without recourse to violence.  If I was forced to explore why I felt that way beyond irritation at general “unfairness” or a fear that I might ultimately become the victim of those things if I didn’t work to oppose them, I had to come to terms with my fear and mistrust of people in groups, and to build a genuine respect, rather than a wary vigilance, for other humans.  Teachers like <a href="https://www.uvic.ca/education/curriculum/faculty-staff/faculty/about/experts/profiles/price-jason.php">Jason Price</a>, <a href="https://www.nipissingu.ca/users/warnie-richardson">Warnie Richardson</a>, and <a href="https://www.johnpportelli.com/">John Portelli</a> were instrumental.</p>

<p>Eventually, I figured out that shared, useful knowledge makes it a lot easier to live peacefully in community and trained critical faculties make it a lot easier to develop, test, discard, and update knowledge independently and with others; that was a vision of democratic society I wanted to contribute to and live inside.  As I’ve aged and learned more, I appreciate better how important the subjectivity of “truth” is in society and how inescapable the plurality of “truth” is in my own psyche<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> — a vision of education and collaborative knowledge-building can’t be so rigid as I’d first imagined without risking authoritarianism and tribalism.  Or at least, <em>I</em> can’t see how it could be.</p>

<p>What remains true for me is that our shared humanity emerges from shared knowledge-building, that felt and considered belief in our shared humanity is the only force that can sustain community, and that community is the only thing that can, at last, sustain <em>us</em>.  When I work with students to help them build an understanding of a concept, to learn and practice a skill, or to experience empowerment through the successful application of their creativity and ambition, that sense of shared humanity, of operation within and in contribution to society, is the background and leitmotif of everything I do.  When I teach well.</p>

<h2 id="why-i-code">Why I Code</h2>

<p>My god, it’s fun.  I came to serious, intentional programming-as-craft relatively recently as a bootcamp student in 2018, so while I’ve been formally (if rapidly) trained in some of the practicalities of programming, my appreciation of craft — computer science, algorithms, aesthetics — has developed on the job through generous mentorship, independent reading, and play.  What drives me, though, is often the proximal satisfaction of transmuting a solved problem into a tool that then expands my creative horizons.  Programming can be at once a meditation on the topography of personal intellectual possibility and its own vehicle to explore and develop that landscape.  How sweet it is that I can do so by solving puzzles and rendering cathedrals.<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup></p>

<h2 id="why-i-work">Why I Work</h2>

<p>I have a tiny son who needs to live in this world.  When I can, I teach and code for the money that will help make his life comfortable and rich, but I can’t avoid seeing how easily my professions poison that world.  Teaching without ethos, humility, or kindness, is pedagoguery, inculcation, and vanity; building tools for others to use — libraries, platforms, philosophies — without circumspection or custodianship, and with greed, mortgages lasting harmony and social sustainability for short-term gain.  I need to know that what I do and for whom I work leaves my son’s world — not just his house — a better place.</p>

<p>That’s the best of me right now.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Not just my own, I hope… <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p><em>Aspirational</em> cathedrals. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Danny Fekete</name><email>danny@neckdeep.dev</email></author><category term="blog" /><category term="philosophy" /><summary type="html"><![CDATA[A longwinded snapshot my personal ethos for reference and self-improvement.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://neckdeep.dev/assets/blog/img/school-of-athens-computer.png" /><media:content medium="image" url="https://neckdeep.dev/assets/blog/img/school-of-athens-computer.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">A Simple Sandwich, Part II</title><link href="https://neckdeep.dev/blog/2022-12-19-a-simple-sandwich-ii/" rel="alternate" type="text/html" title="A Simple Sandwich, Part II" /><published>2022-12-19T00:00:00+00:00</published><updated>2023-01-13T17:41:42+00:00</updated><id>https://neckdeep.dev/blog/a-simple-sandwich-ii</id><content type="html" xml:base="https://neckdeep.dev/blog/2022-12-19-a-simple-sandwich-ii/"><![CDATA[<blockquote class="note smaller" title="Continuity Alert">
  <p>This post is the second part of a brief, pointless series in which I explore how to make a sandwich in Ruby in response to a video where a father maliciously and capriciously complies with his children’s well-intentioned sandwich-building instructions.</p>

  <p>If you haven’t read <a href="/blog/2022-12-13-a-simple-sandwich-i/" class="heading flip-title">Part I</a> yet, you should probably do that first.</p>

  <p>… And, if you’d prefer to just read the code and judge me, here’s the <a href="https://gist.github.com/StandardGiraffe/b946bc787def30807fe2f68de846d0c3/2c04c82f60d0bd45f581a4d707c356beb09d6220">gist</a>.</p>
</blockquote>

<p>In servile accordance with the dictates of whimsy, I have formed a universe for making sandwiches.  But as we have seen, sandwiches are hard: there are many components and tools and avenues for failure.  So, when we last considered the myriad fail-states, I resolved to create an intelligence that would ensure smooth operation.  Practically speaking, I want to build a class that leverages knowledge of the system so that it can make sandwiches <em>correctly</em>, at which point I can just ask it to <code class="language-plaintext highlighter-rouge">make_me_a_sandwich</code>.</p>

<p>If the dad in the previous post’s video had been taking his role as patriarch and provider seriously<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>, what we <em>should</em> have seen was:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">daddy</span> <span class="o">=</span> <span class="no">Chef</span><span class="p">.</span><span class="nf">new</span>

<span class="n">lunch</span> <span class="o">=</span> <span class="n">daddy</span><span class="p">.</span><span class="nf">make_me_a_sandwich!</span><span class="p">(</span><span class="s2">"Peanut Butter"</span><span class="p">,</span> <span class="s2">"Jelly"</span><span class="p">)</span>

<span class="c1">#=&gt; Poof: you're a sandwich. (Chef::TakenForGrantedError)</span>
</code></pre></div></div>

<p>… Well.  Courtesy is important.  What we <em>should</em> have seen was:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">daddy</span> <span class="o">=</span> <span class="no">Chef</span><span class="p">.</span><span class="nf">new</span>

<span class="n">lunch</span> <span class="o">=</span> <span class="n">daddy</span><span class="p">.</span><span class="nf">please</span><span class="p">.</span><span class="nf">make_me_a_sandwich!</span><span class="p">(</span><span class="s2">"Peanut Butter"</span><span class="p">,</span> <span class="s2">"Jelly"</span><span class="p">)</span>

<span class="n">pp</span> <span class="n">lunch</span>

<span class="c1">#=&gt;</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#&lt;Sandwich:0x00007feb529d4398
 @built=true,
 @cut=true,
 @slices=
  [#&lt;SliceOfBread:0x00007feb529d4c80
    @bottom=#&lt;SliceOfBread::Surface:0x00007feb529d4a78 @contents=nil&gt;,
    @top=
     #&lt;SliceOfBread::Surface:0x00007feb529d4bb8 @contents="Peanut Butter"&gt;&gt;,
   #&lt;SliceOfBread:0x00007feb529d6080
    @bottom=#&lt;SliceOfBread::Surface:0x00007feb529d4d98 @contents="Jelly"&gt;,
    @top=#&lt;SliceOfBread::Surface:0x00007feb529d5838 @contents=nil&gt;&gt;]&gt;
</code></pre></div></div>

<ul id="markdown-toc">
  <li><a href="#my-two-dads" id="markdown-toc-my-two-dads">My Two Dads</a></li>
  <li><a href="#initialization-and-pleasantries" id="markdown-toc-initialization-and-pleasantries">Initialization and Pleasantries</a></li>
  <li><a href="#do-it" id="markdown-toc-do-it">Do it.</a>    <ul>
      <li><a href="#sanity-check" id="markdown-toc-sanity-check">Sanity Check</a></li>
      <li><a href="#dont-mess-up" id="markdown-toc-dont-mess-up">Don’t mess up.</a>        <ul>
          <li><a href="#condiment-get" id="markdown-toc-condiment-get">Condiment-Get</a></li>
          <li><a href="#bread-get" id="markdown-toc-bread-get">Bread-Get</a></li>
        </ul>
      </li>
      <li><a href="#compose-food" id="markdown-toc-compose-food">Compose Food</a></li>
    </ul>
  </li>
  <li><a href="#inevitable-success" id="markdown-toc-inevitable-success">Inevitable Success</a></li>
</ul>

<h2 id="my-two-dads">My Two Dads</h2>

<p>As I built my <code class="language-plaintext highlighter-rouge">Chef</code> class, I struggled between two approaches for a while.  And, although I landed on the version you’ll see in this article, it’s been suggested to me that I address both (and I’ll ultimately try to build both).</p>

<p>Today’s <code class="language-plaintext highlighter-rouge">Chef</code> operates in a way that resembles my <a href="https://torontoguardian.com/2018/09/chef-andrew-piccinin-parcheggio/">brother-in-law</a> and my wife when <em>they</em> cook – at least from <em>my</em> perspective: because they command all the necessary (arcane) knowledge for the cooking task ahead of time, their planning and preparation circumvents all conceivable procedural problems.  That is, there’s no room for mistakes because the tools, environment, and request are all vetted and corrected before the operation begins; all that remains is to watch a glorious monument to competence and inevitability construct itself and deliver delicious success.</p>

<p>I appreciate that by pedestalling my family this way I both essentialize the skill and intelligence they’ve worked years to build <em>and</em> I ignore the ongoing creativity and problem-solving they do while they work and encounter (even for <em>them</em>) unexpected complications.  … But it’s <em>my</em> class and I know how to build a sandwich in <em>my</em> universe, so <em>I’ll</em> construct a flawless sandwich maker.  Crucially, this sandwich maker will never encounter the exception classes I built in the previous article; it doesn’t know they exist and can’t <code class="language-plaintext highlighter-rouge">rescue</code> them, but <em>as long as the nature of sandwiches in my universe never changes</em>, that won’t matter.  This is a brittle approach, but for now, we’ll get a sandwich.</p>

<h2 id="initialization-and-pleasantries">Initialization and Pleasantries</h2>
<p>Let’s start with the exceptions and initializer for  <code class="language-plaintext highlighter-rouge">Chef</code>.  Instances of <code class="language-plaintext highlighter-rouge">Chef</code> will manipulate the components of our sandwich universe, so they should have the option of initializing with those components and will store them as the properties <code class="language-plaintext highlighter-rouge">@condiments</code>, <code class="language-plaintext highlighter-rouge">@knife</code>, and <code class="language-plaintext highlighter-rouge">@slices_of_bread</code>.</p>

<p>I’ve also built in a sense of self-respect with <code class="language-plaintext highlighter-rouge">@under_appreciated</code> because courtesy is important.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Chef</span>
  <span class="k">class</span> <span class="nc">TakenForGrantedError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>
  <span class="k">class</span> <span class="nc">UnspeakablyBlandError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>

  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="ss">condiments: </span><span class="p">[],</span> <span class="ss">knife: </span><span class="kp">nil</span><span class="p">,</span> <span class="ss">slices_of_bread: </span><span class="p">[])</span>
    <span class="vi">@condiments</span> <span class="o">=</span> <span class="n">condiments</span>
    <span class="vi">@knife</span> <span class="o">=</span> <span class="n">knife</span>
    <span class="vi">@slices_of_bread</span> <span class="o">=</span> <span class="n">slices_of_bread</span>

    <span class="vi">@under_appreciated</span> <span class="o">=</span> <span class="kp">true</span>
  <span class="k">end</span>

<span class="c1"># ...</span>
</code></pre></div></div>

<p>Courtesy is easy in this universe:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Chef</span>
  <span class="c1"># ...</span>

  <span class="k">def</span> <span class="nf">please</span>
    <span class="vi">@under_appreciated</span> <span class="o">=</span> <span class="kp">false</span>

    <span class="nb">self</span>
  <span class="k">end</span>

  <span class="c1"># ...</span>
</code></pre></div></div>

<p>(<code class="language-plaintext highlighter-rouge">Chef#please</code> returns the instance (<code class="language-plaintext highlighter-rouge">self</code>) in order to allow for chaining, as in <code class="language-plaintext highlighter-rouge">daddy.please.&lt;carry_on&gt;</code>.)  The only public method left to cover, then, is <code class="language-plaintext highlighter-rouge">#make_me_a_sandwich!</code>.</p>

<h2 id="do-it">Do it.</h2>
<p><a href="https://xkcd.com/149/">Okay</a>.</p>

<h3 id="sanity-check">Sanity Check</h3>
<p><code class="language-plaintext highlighter-rouge">Chef#make_me_a_sandwich!</code> takes an arbitrary number of arguments to represent the desired smearables; these requests should come in as simple strings to represent the fact that the user (ravenous child, etc.) doesn’t need to have deep knowledge about the structure and procedures of Sandwich Universe to make the request: <code class="language-plaintext highlighter-rouge">Chef</code> will take care of it.  What <em>is</em> needed is a sane request (at least <em>one</em> non-<code class="language-plaintext highlighter-rouge">nil</code>, <code class="language-plaintext highlighter-rouge">String</code>-type flavour).</p>

<p>And, you know.  That other important thing.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Chef</span>
  <span class="c1"># ...</span>

  <span class="k">def</span> <span class="nf">make_me_a_sandwich!</span><span class="p">(</span><span class="o">*</span><span class="n">requested_flavours</span><span class="p">)</span>
    <span class="k">if</span> <span class="vi">@under_appreciated</span>  <span class="c1"># Courtesy is important!</span>
      <span class="k">raise</span> <span class="no">TakenForGrantedError</span><span class="p">,</span> <span class="s2">"Poof: you're a sandwich."</span>
    <span class="k">end</span>

    <span class="vi">@under_appreciated</span> <span class="o">=</span> <span class="kp">true</span>  <span class="c1"># You get ONE request per courtesy.</span>

    <span class="c1"># No vacuum- or integer-sandwiches please; they give me a headache.</span>
    <span class="n">requested_flavours</span><span class="p">.</span><span class="nf">keep_if</span> <span class="p">{</span> <span class="o">|</span><span class="n">flavour</span><span class="o">|</span> <span class="n">flavour</span><span class="p">.</span><span class="nf">is_a?</span> <span class="no">String</span> <span class="p">}</span>

    <span class="k">if</span> <span class="n">requested_flavours</span><span class="p">.</span><span class="nf">count</span> <span class="o">&lt;</span> <span class="mi">1</span>
      <span class="k">raise</span> <span class="no">UnspeakablyBlandError</span><span class="p">,</span> <span class="s2">"I think you might just want some dry toast."</span>
    <span class="k">end</span>

    <span class="c1"># ...</span>
</code></pre></div></div>

<p>These gate conditions account for the two custom exceptions <code class="language-plaintext highlighter-rouge">Chef</code> knows: <code class="language-plaintext highlighter-rouge">TakenForGrantedError</code> and <code class="language-plaintext highlighter-rouge">UnspeakablyBlandError</code>.  If we’ve made it through <em>that</em> gauntlet, we are <em>guaranteed</em> a sandwich, says <code class="language-plaintext highlighter-rouge">Chef</code>.</p>

<h3 id="dont-mess-up">Don’t mess up.</h3>
<p>Here’s how:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Chef</span>
  <span class="c1"># ...</span>

  <span class="k">def</span> <span class="nf">make_me_a_sandwich!</span><span class="p">(</span><span class="o">*</span><span class="n">requested_flavours</span><span class="p">)</span>
    <span class="c1"># ... Gate Conditions / Sanity Checks Done</span>

    <span class="c1"># Let's make sure we've got what we need and fix any problems.</span>
    <span class="n">ensure_condiments_are_actually_condiments</span>
    <span class="n">ensure_bread_is_actually_bread</span>
    <span class="n">ensure_knife</span>

    <span class="n">ensure_condiments_are_available</span><span class="p">(</span><span class="n">requested_flavours</span><span class="p">)</span>
    <span class="n">ensure_enough_bread_is_available</span><span class="p">(</span><span class="n">requested_flavours</span><span class="p">)</span>

    <span class="c1"># ...</span>
</code></pre></div></div>

<p>This series of private method calls serves as a checklist to make sure the inventory of the particular <code class="language-plaintext highlighter-rouge">Chef</code> instance taking the order is sufficiently stocked.<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>  The first three will discard any unsuitable components that might already be on-hand and confuse the process (with <code class="language-plaintext highlighter-rouge">#ensure_knife</code> taking the extra step of grabbing one if needed):</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Chef</span>
  <span class="c1"># ...</span>
<span class="kp">private</span>
  <span class="c1"># Throw out any empty bottles and/or cans of paint.</span>
  <span class="k">def</span> <span class="nf">ensure_condiments_are_actually_condiments</span>
    <span class="vi">@condiments</span><span class="p">.</span><span class="nf">keep_if</span> <span class="k">do</span> <span class="o">|</span><span class="n">condiment</span><span class="o">|</span>
      <span class="n">condiment</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">CondimentJar</span><span class="p">)</span> <span class="n">and</span> <span class="n">condiment</span><span class="p">.</span><span class="nf">has_stuff?</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="c1"># Keep our existing bread collection sane and unsullied.</span>
  <span class="k">def</span> <span class="nf">ensure_bread_is_actually_bread</span>
    <span class="vi">@slices_of_bread</span><span class="p">.</span><span class="nf">keep_if</span> <span class="k">do</span> <span class="o">|</span><span class="n">slice</span><span class="o">|</span>
      <span class="n">slice</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">SliceOfBread</span><span class="p">)</span> <span class="n">and</span> <span class="n">slice</span><span class="p">.</span><span class="nf">plain?</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="c1"># Safety first.</span>
  <span class="k">def</span> <span class="nf">ensure_knife</span>
    <span class="k">return</span> <span class="k">if</span> <span class="vi">@knife</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">Knife</span><span class="p">)</span>

    <span class="nb">puts</span> <span class="s2">"I'll probably want a knife for this..."</span>

    <span class="vi">@knife</span> <span class="o">=</span> <span class="no">Knife</span><span class="p">.</span><span class="nf">new</span>

    <span class="nb">puts</span> <span class="s2">"... Full tang mokume gane butter knife.  Perfect."</span>
  <span class="k">end</span>

  <span class="c1"># ...</span>
</code></pre></div></div>

<h4 id="condiment-get">Condiment-Get</h4>

<p>The remaining insurance methods are more sensitive to the specific nature of the requested sandwich, so let’s look at them individually.  The easy one is <code class="language-plaintext highlighter-rouge">#ensure_condiments_are_available</code>: given the requested flavours, check what’s on hand and go to the store to pick up anything that’s missing.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Chef</span>
  <span class="c1"># ...</span>
<span class="kp">private</span>
  <span class="c1"># ...</span>

  <span class="c1"># Gather any missing condiments...</span>
  <span class="k">def</span> <span class="nf">ensure_condiments_are_available</span><span class="p">(</span><span class="n">requested_flavours</span><span class="p">)</span>
    <span class="n">missing_condiments</span> <span class="o">=</span> <span class="p">[</span> <span class="p">]</span>

    <span class="c1"># Let's avoid multiple trips to the store by taking stock before we go.</span>
    <span class="n">requested_flavours</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">flavour</span><span class="o">|</span>
      <span class="k">unless</span> <span class="vi">@condiments</span><span class="p">.</span><span class="nf">find</span> <span class="p">{</span> <span class="o">|</span><span class="n">jar</span><span class="o">|</span> <span class="n">jar</span><span class="p">.</span><span class="nf">contents</span> <span class="o">==</span> <span class="n">flavour</span><span class="p">}</span>
        <span class="n">missing_condiments</span> <span class="o">&lt;&lt;</span> <span class="n">flavour</span>
      <span class="k">end</span>
    <span class="k">end</span>

    <span class="k">if</span> <span class="n">missing_condiments</span><span class="p">.</span><span class="nf">any?</span>
      <span class="nb">puts</span> <span class="s2">"Looks like we could use a few things from the store..."</span>

      <span class="n">missing_condiments</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">condiment</span><span class="o">|</span>
        <span class="vi">@condiments</span> <span class="o">&lt;&lt;</span> <span class="no">CondimentJar</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">condiment</span><span class="p">)</span>
        <span class="nb">puts</span> <span class="s2">"Bought me some </span><span class="si">#{</span><span class="n">condiment</span><span class="si">}</span><span class="s2">"</span>
      <span class="k">end</span>

      <span class="nb">puts</span> <span class="s2">"There we go.  That's better."</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="c1"># ...</span>
</code></pre></div></div>

<h4 id="bread-get">Bread-Get</h4>

<p>The final method, <code class="language-plaintext highlighter-rouge">#ensure_enough_bread_is_available</code>, requires a bit of knowledge about Sandwichness.  As a quick recap, we’re treating a valid sandwich as one which has at least two slices of bread with the outside surfaces plain, at least one condiment smeared on an inside surface, and only one condiment to an inside surface.  There are to be no plain slices of bread in our sandwich except possibly the final slice (because if the last smeared surface was the top of a slice, the sandwich will need an extra, unsmeary “hat” to complete it).</p>

<p>This means the following holds true:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center"><strong>Number of Condiments</strong></th>
      <th style="text-align: center"><strong>Number of Slices</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">0</td>
      <td style="text-align: center">n/a (not a sandwich)</td>
    </tr>
    <tr>
      <td style="text-align: center">1</td>
      <td style="text-align: center">2 (top slice plain)</td>
    </tr>
    <tr>
      <td style="text-align: center">2</td>
      <td style="text-align: center">2 (inside surfaces smeared)</td>
    </tr>
    <tr>
      <td style="text-align: center">3</td>
      <td style="text-align: center">3 (top slice plain)</td>
    </tr>
    <tr>
      <td style="text-align: center">4</td>
      <td style="text-align: center">3</td>
    </tr>
    <tr>
      <td style="text-align: center">5</td>
      <td style="text-align: center">4 (top slice plain)</td>
    </tr>
    <tr>
      <td style="text-align: center">6</td>
      <td style="text-align: center">4</td>
    </tr>
    <tr>
      <td style="text-align: center">7</td>
      <td style="text-align: center">5 (top slice plain)</td>
    </tr>
    <tr>
      <td style="text-align: center">8</td>
      <td style="text-align: center">5</td>
    </tr>
  </tbody>
</table>

<p>So, to figure out how much bread is required for a sandwich with <em>n</em> slices, we take half the number of condiments, round up, and add one.  Then, just grab bread from the bag until we’ve got the required amount:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Chef</span>
  <span class="c1"># ...</span>
<span class="kp">private</span>
  <span class="c1"># ...</span>

  <span class="c1"># Make sure we won't run out in the middle of the operation.</span>
  <span class="k">def</span> <span class="nf">ensure_enough_bread_is_available</span><span class="p">(</span><span class="n">requested_flavours</span><span class="p">)</span>
    <span class="n">sufficient_slices</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="p">(</span><span class="n">requested_flavours</span><span class="p">.</span><span class="nf">count</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">).</span><span class="nf">ceil</span>
    <span class="k">return</span> <span class="k">if</span> <span class="vi">@slices_of_bread</span><span class="p">.</span><span class="nf">count</span> <span class="o">&gt;=</span> <span class="n">sufficient_slices</span>

    <span class="nb">puts</span> <span class="s2">"This isn't enough bread to make your sandwich.  Let me just grab some more."</span>

    <span class="k">while</span> <span class="vi">@slices_of_bread</span><span class="p">.</span><span class="nf">count</span> <span class="o">&lt;</span> <span class="n">sufficient_slices</span>
      <span class="vi">@slices_of_bread</span> <span class="o">&lt;&lt;</span> <span class="no">SliceOfBread</span><span class="p">.</span><span class="nf">new</span>
      <span class="nb">puts</span> <span class="s2">"Yoink!"</span>
    <span class="k">end</span>

    <span class="nb">puts</span> <span class="s2">"... There we go.  Pile o' bread!"</span>
  <span class="k">end</span>
</code></pre></div></div>

<h3 id="compose-food">Compose Food</h3>
<p>Returning to <code class="language-plaintext highlighter-rouge">#make_me_a_sandwich!</code> , we’ve now created a safe environment in which a competent sandwich artisan could get on with business.</p>

<ul>
  <li>Starting with a blank sandwich, we’ll iterate through our condiments and smear slices of bread, starting with the top surface of the first slice (leaving the bottom plain and unyucky to hold) and then alternating surfaces until we run out of ingredients.</li>
  <li>Whenever we wind up with a slice that has its top surface smeared, we’ll add it to the sandwich stack and grab some fresh bread.</li>
  <li>Once all the condiments have been applied and slices stacked, if the top of the stack is smeary, we’ll slap on a final slice of bread.</li>
  <li><code class="language-plaintext highlighter-rouge">build!</code>, <code class="language-plaintext highlighter-rouge">cut!</code>, see that it is good, and set it free.</li>
</ul>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Chef</span>
  <span class="c1"># ...</span>

  <span class="k">def</span> <span class="nf">make_me_a_sandwich!</span><span class="p">(</span><span class="o">*</span><span class="n">requested_flavours</span><span class="p">)</span>
    <span class="c1"># Gate Conditions / Sanity Checks Done ...</span>
    <span class="c1"># Insurance Methods Done ...</span>

    <span class="c1"># Create an empty proto-sandwich.  Really, just the idea of a sandwich.</span>
    <span class="n">sandwich</span> <span class="o">=</span> <span class="no">Sandwich</span><span class="p">.</span><span class="nf">new</span>

    <span class="vi">@knife</span><span class="p">.</span><span class="nf">clean!</span>

    <span class="n">surface_to_smear</span> <span class="o">=</span> <span class="ss">:top</span>
    <span class="n">current_slice</span> <span class="o">=</span> <span class="vi">@slices_of_bread</span><span class="p">.</span><span class="nf">pop</span>

    <span class="n">requested_flavours</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">flavour</span><span class="o">|</span>
      <span class="n">condiment</span> <span class="o">=</span> <span class="vi">@condiments</span><span class="p">.</span><span class="nf">find</span> <span class="p">{</span> <span class="o">|</span><span class="n">jar</span><span class="o">|</span> <span class="n">jar</span><span class="p">.</span><span class="nf">contents</span> <span class="o">==</span> <span class="n">flavour</span><span class="p">}</span>
      <span class="n">condiment</span><span class="p">.</span><span class="nf">open!</span>

      <span class="vi">@knife</span><span class="p">.</span><span class="nf">load_from!</span><span class="p">(</span><span class="n">condiment</span><span class="p">)</span>

      <span class="n">current_slice</span><span class="p">.</span><span class="nf">smear!</span><span class="p">(</span>
        <span class="ss">knife: </span><span class="vi">@knife</span><span class="p">,</span>
        <span class="ss">surface: </span><span class="n">surface_to_smear</span>
      <span class="p">)</span>

      <span class="c1"># If ready, add the current slice to the stack and grab another.</span>
      <span class="k">if</span> <span class="n">current_slice</span><span class="p">.</span><span class="nf">top</span><span class="p">.</span><span class="nf">smeared?</span>
        <span class="n">sandwich</span> <span class="o">&lt;&lt;</span> <span class="n">current_slice</span>
        <span class="n">current_slice</span> <span class="o">=</span> <span class="vi">@slices_of_bread</span><span class="p">.</span><span class="nf">pop</span>
      <span class="k">end</span>

      <span class="c1"># Swap the target surface for the next condiment.</span>
      <span class="n">surface_to_smear</span> <span class="o">=</span> <span class="p">(</span><span class="n">surface_to_smear</span> <span class="o">==</span> <span class="ss">:top</span> <span class="p">?</span> <span class="ss">:bottom</span> <span class="p">:</span> <span class="ss">:top</span><span class="p">)</span>
    <span class="k">end</span>

    <span class="c1"># Add the final slice</span>
    <span class="n">sandwich</span> <span class="o">&lt;&lt;</span> <span class="n">current_slice</span>

    <span class="c1"># The magic happens</span>
    <span class="n">sandwich</span><span class="p">.</span><span class="nf">build!</span>
    <span class="n">sandwich</span><span class="p">.</span><span class="nf">cut!</span><span class="p">(</span><span class="vi">@knife</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">sandwich</span><span class="p">.</span><span class="nf">ready_to_eat?</span>  <span class="c1"># How could it not be? Still.</span>
      <span class="nb">puts</span> <span class="s2">"Finit.  One </span><span class="si">#{</span><span class="n">sandwich</span><span class="p">.</span><span class="nf">flavours_human_readable</span><span class="si">}</span><span class="s2"> sandwich; bon appétit!"</span>

      <span class="k">return</span> <span class="n">sandwich</span>  <span class="c1"># Needlessly explicit "return"; assume to be flourish.</span>
    <span class="k">else</span>
      <span class="nb">puts</span> <span class="s2">"Hmm.  Something went wrong despite my genius."</span>
    <span class="k">end</span>
  <span class="k">end</span>
</code></pre></div></div>

<h2 id="inevitable-success">Inevitable Success</h2>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">daddy</span> <span class="o">=</span> <span class="no">Chef</span><span class="p">.</span><span class="nf">new</span>

<span class="n">lunch</span> <span class="o">=</span> <span class="n">daddy</span><span class="p">.</span><span class="nf">please</span><span class="p">.</span><span class="nf">make_me_a_sandwich!</span><span class="p">(</span>
  <span class="s2">"Relish"</span><span class="p">,</span>
  <span class="s2">"Marmite™"</span><span class="p">,</span>
  <span class="s2">"Nutella™"</span><span class="p">,</span>
  <span class="s2">"Sriracha"</span>
<span class="p">)</span>

<span class="c1">#=&gt;</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>I'll probably want a knife for this...
... Full tang mokume gane butter knife.  Perfect.
Looks like we could use a few things from the store...
Bought me some Relish
Bought me some Marmite™
Bought me some Nutella™
Bought me some Sriracha
There we go.  That's better.
This isn't enough bread to make your sandwich.  Let me just grab some more.
Yoink!
Yoink!
Yoink!
... There we go.  Pile o' bread!
Finit.  One relish, marmite™, nutella™, and sriracha sandwich; bon appétit!
</code></pre></div></div>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">pp</span> <span class="n">lunch</span>

<span class="c1">#=&gt;</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#&lt;Sandwich:0x00007f0ced4a2d78
 @built=true,
 @cut=true,
 @slices=
  [#&lt;SliceOfBread:0x00007f0ced4a31d8
    @bottom=#&lt;SliceOfBread::Surface:0x00007f0ced4a30e8 @contents=nil&gt;,
    @top=#&lt;SliceOfBread::Surface:0x00007f0ced4a3138 @contents="Relish"&gt;&gt;,
   #&lt;SliceOfBread:0x00007f0ced4a3408
    @bottom=#&lt;SliceOfBread::Surface:0x00007f0ced4a3318 @contents="Marmite™"&gt;,
    @top=#&lt;SliceOfBread::Surface:0x00007f0ced4a33b8 @contents="Nutella™"&gt;&gt;,
   #&lt;SliceOfBread:0x00007f0ced4a38e0
    @bottom=#&lt;SliceOfBread::Surface:0x00007f0ced4a36b0 @contents="Sriracha"&gt;,
    @top=#&lt;SliceOfBread::Surface:0x00007f0ced4a3840 @contents=nil&gt;&gt;]&gt;
</code></pre></div></div>

<p>Again, while this approach furnishes the user a mouth-wateringly valid sandwich smeared with one or more condiments, it feels like a bit of a cheat: <code class="language-plaintext highlighter-rouge">Chef</code> never discovers a missing ingredient or misconfigured spreading implement while up to its elbows in proto-food (the way I do); <code class="language-plaintext highlighter-rouge">Chef</code> never needs to problem-solve on the fly, heroically rescuing a meal from self-induced inedibility (the way I do); <code class="language-plaintext highlighter-rouge">Chef</code> never needs to cry.</p>

<p>We should fix that.</p>

<p class="read-more">… But first, <a href="/blog/2023-01-09-a-simple-sandwich-contorno/" class="heading flip-title">Contorno</a></p>

<hr />
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>… And was made out of Ruby code… <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>Incidentally, the checklist is order-dependent as it’s presented here which adds a bit of unneeded brittleness.  It would be smarter for me to call some of these methods within the other methods (like <code class="language-plaintext highlighter-rouge">#ensure_bread_is_actually_bread</code> as the first step of <code class="language-plaintext highlighter-rouge">#ensure_enough_bread_is_available</code> so that nothing can contaminate my precious loaf between steps); with that caveat, the checklist in my example is exploded because it’s a bit more intuitive to follow, I hope. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Danny Fekete</name><email>danny@neckdeep.dev</email></author><category term="blog" /><category term="programming" /><summary type="html"><![CDATA[Adding competence, if not intelligence, to a heartless universe.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://neckdeep.dev/assets/blog/sandwich/img/sandwich-dad.png" /><media:content medium="image" url="https://neckdeep.dev/assets/blog/sandwich/img/sandwich-dad.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">A Simple Sandwich, Part I</title><link href="https://neckdeep.dev/blog/2022-12-13-a-simple-sandwich-i/" rel="alternate" type="text/html" title="A Simple Sandwich, Part I" /><published>2022-12-13T00:00:00+00:00</published><updated>2023-01-13T17:41:42+00:00</updated><id>https://neckdeep.dev/blog/a-simple-sandwich-i</id><content type="html" xml:base="https://neckdeep.dev/blog/2022-12-13-a-simple-sandwich-i/"><![CDATA[<p>In the maelstrom of Reddit and Twitter links that constitutes my family Signal channel, my brother-in-law shared this video, asking “Danny, is this programmer humour?”</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/FN2RM-CHkuI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p>Andrew’s question was tricky for me: the kids’ experience is often <em>my</em> experience when I program, yet when it <em>is</em> my experience, THERE IS NOTHING FUNNY ABOUT IT.  But yes, it’s easy to make an analogy to computers here: a computer will tirelessly, uncomplainingly, uncreatively, unintuitively, bloodymindedly follow your instructions as written (so long as they’re written in a shared language), so any failure to achieve desired results necessarily devolves to <em>your</em> failure to give good instructions.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></p>

<p>Watching the video, though, actually got me thinking more about education than programming.  During Curriculum Nights at Dunblaine, April Turner, the teacher of the youngest classroom, often leads the parents of her students through a process similar to that shown in the video in order to help them appreciate what it feels like to operate with executive dysfunction.  Many of our kids, through inexperience or exceptionality, get overwhelmed by the granular elements of what most people would consider <em>simple</em> tasks.  If everything you try to do is crushingly complicated, easily derailed by error, and demands most or all (or more than all) of your working memory to perform, it becomes easier to see why these tasks take longer than they “should”, why kids get exhausted or give up “too easily” and develop learned helplessness, and why disengagement and despondency follow.</p>

<p>Reducing complexity through abstraction is a good strategy in teaching as in programming.  For overwhelmed students, “chunking” tasks into rehearsable (and masterable) components, and then accreting small chunks into larger chunks, can eventually help them go from “stick the flat edge of the knife into the previously-opened jar and then drag it around until the condiment adheres to it and then withdraw it and then rub the adhered condiment as evenly as possible against an exposed broad surface of the bread (but not so hard that the bread tears (moderate application strength with reference to the relative densities of the bread and condiment (also, if applicable, account for treacherous and brittle resilience due to the degree of toastedness)))” to “butter the bread”.  Similarly, in <em>programming</em>, we can avoid getting overwhelmed by abstracting chunks of code into functions.</p>

<p>With reference to the video, I observed to Andrew that <code class="language-plaintext highlighter-rouge">open_container(container)</code> seems to be in Dad’s standard library<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>, but he doesn’t seem to have a <code class="language-plaintext highlighter-rouge">load_knife(container)</code> or <code class="language-plaintext highlighter-rouge">spread_condiment(knife, condiment, bread)</code> function handy, so when he gets to these steps, he follows inconsistently implemented and often unreliable instructions, alas.</p>

<p>From Andrew, this earned me:</p>

<p><img src="/assets/blog/sandwich/img/twitching-eye.gif" alt="Twitching Eye Squirrel" /></p>

<p>… Which is fair.  I’m not sure Andrew’s familiar with the <code class="language-plaintext highlighter-rouge">&lt;function_name&gt;(&lt;parameters&gt;)&gt;</code> syntax or my assumed (perhaps unwise) logical flow of passing objects to functions as arguments<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>.  So, instead, I tried to illustrate the process with objects and methods: I love how Ruby can often look like plain English and I wanted to share this with him.<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup></p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">load_knife</span><span class="p">(</span><span class="n">knife</span><span class="p">,</span> <span class="n">container</span><span class="p">)</span>
  <span class="k">if</span> <span class="n">knife</span><span class="p">.</span><span class="nf">contents</span><span class="p">.</span><span class="nf">present?</span>
    <span class="k">raise</span> <span class="s2">"Error!  This knife already has something on it.  Yuck!"</span>
  <span class="k">end</span>

  <span class="k">if</span> <span class="n">container</span><span class="p">.</span><span class="nf">closed?</span>
    <span class="n">container</span><span class="p">.</span><span class="nf">open!</span>
  <span class="k">end</span>

  <span class="k">if</span> <span class="n">container</span><span class="p">.</span><span class="nf">contents</span><span class="p">.</span><span class="nf">nil?</span>
    <span class="k">raise</span> <span class="s2">"Error!  This container is empty!"</span>
  <span class="k">end</span>

  <span class="n">knife</span><span class="p">.</span><span class="nf">contents</span> <span class="o">=</span> <span class="n">container</span><span class="p">.</span><span class="nf">contents</span>

  <span class="k">return</span> <span class="n">knife</span>
<span class="k">end</span>
</code></pre></div></div>

<p>To me, this seemed much clearer (just so long as you don’t actually program in Ruby; then, it probably just raises questions and code smells).  Yet, Andrew was unilluminated and memed:
<img src="/assets/blog/sandwich/img/head-shake-no.gif" alt="Nope" /></p>

<p>Sandwiches are hard.  And, like a good teacher, I gave up on him and left him to <a href="https://torontoguardian.com/2018/09/chef-andrew-piccinin-parcheggio/">continue managing his restaurants</a>.<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup> … But I developed an itch to take a real shot at some sandwich-making code.  And you’re still here, whoever you are, so let’s make a sandwich.</p>

<hr />
<ul class="large-only" id="markdown-toc">
  <li><a href="#building-the-blueprints" id="markdown-toc-building-the-blueprints">Building the Blueprints</a>    <ul>
      <li><a href="#the-matter" id="markdown-toc-the-matter">The Matter</a></li>
      <li><a href="#the-vector" id="markdown-toc-the-vector">The Vector</a></li>
      <li><a href="#the-medium" id="markdown-toc-the-medium">The Medium</a></li>
      <li><a href="#sandwichness" id="markdown-toc-sandwichness">Sandwichness</a></li>
      <li><a href="#the-problem-unsolved" id="markdown-toc-the-problem-unsolved">The Problem Unsolved</a></li>
    </ul>
  </li>
</ul>
<h2 id="building-the-blueprints">Building the Blueprints</h2>

<p>Now, in Rubyland, if you want to make a sandwich from scratch, <a href="https://www.youtube.com/watch?v=BkHCO8f2TWs">you must first invent the universe</a>.  Or at least some classes.  I wanted to start by reaching parity with my back-of-the-napkin assertions above.  So we need the concept of a condiment jar, a knife, and some bread.</p>

<blockquote class="note smaller" title="A note regarding my use of exceptions...">
  <p>An approach you’ll see me using here is to start my classes with namespaced custom exception declarations that inherit from <code class="language-plaintext highlighter-rouge">StandardError</code>. While I (rather, one) could make them function in bespoke and baroque ways, for our purposes, <em>you can treat these as a list of things that could go wrong</em>.</p>
  <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">PaneOfRawSheetGlass</span>
  <span class="k">class</span> <span class="nc">ButterFingersError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>  <span class="c1"># Oh no.</span>
  <span class="k">class</span> <span class="nc">OpenManHoleError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>  <span class="c1"># Oh geez.</span>
  <span class="k">class</span> <span class="nc">SpecialAgentFootchaseError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>  <span class="c1"># Oh my.</span>
<span class="k">end</span>
</code></pre></div>  </div>
  <p>Deeper inside the classes, you’ll see me <code class="language-plaintext highlighter-rouge">raise</code> them with an explanatory message that gets printed to the console if the exception isn’t ultimately rescued elsewhere.</p>
</blockquote>

<h3 id="the-matter">The Matter</h3>

<p>Here’s a <code class="language-plaintext highlighter-rouge">CondimentJar</code> blueprint.  It can be opened, it can be full, and it can be empty.  Importantly, it can relinquish its contents.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">CondimentJar</span>
  <span class="k">class</span> <span class="nc">ContainerClosedError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>
  <span class="k">class</span> <span class="nc">ContainerEmptyError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>

  <span class="nb">attr_accessor</span> <span class="ss">:contents</span>

  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">contents</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">)</span>
    <span class="vi">@contents</span> <span class="o">=</span> <span class="n">contents</span>  <span class="c1"># Jar is empty by default.</span>
    <span class="vi">@state_of_the_lid</span> <span class="o">=</span> <span class="ss">:closed</span>  <span class="c1"># I wasn't raised in a barn.</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">empty?</span>
    <span class="n">contents</span><span class="p">.</span><span class="nf">nil?</span>
  <span class="k">end</span>

  <span class="c1"># All jars contain one portion for simplicity.</span>
  <span class="k">def</span> <span class="nf">has_stuff?</span>
    <span class="o">!</span><span class="n">empty?</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">closed?</span>
    <span class="vi">@state_of_the_lid</span> <span class="o">==</span> <span class="ss">:closed</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">close!</span>
    <span class="vi">@state_of_the_lid</span> <span class="o">=</span> <span class="ss">:closed</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">open?</span>
    <span class="vi">@state_of_the_lid</span> <span class="o">==</span> <span class="ss">:open</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">open!</span>
    <span class="vi">@state_of_the_lid</span> <span class="o">=</span> <span class="ss">:open</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">relinquish_condiment!</span>
    <span class="k">if</span> <span class="n">closed?</span>
      <span class="k">raise</span> <span class="no">ContainerClosedError</span><span class="p">,</span> <span class="s2">"The jar is closed and knife-impermeable."</span>
    <span class="k">end</span>

    <span class="k">if</span> <span class="n">empty?</span>
      <span class="k">raise</span> <span class="no">ContainerEmptyError</span><span class="p">,</span> <span class="s2">"The jar is empty.  How disappointing."</span>
    <span class="k">end</span>

    <span class="c1"># Provide the contents and empty the container</span>
    <span class="k">return</span> <span class="n">contents</span><span class="p">.</span><span class="nf">tap</span> <span class="p">{</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span> <span class="nb">self</span><span class="p">.</span><span class="nf">contents</span> <span class="o">=</span> <span class="kp">nil</span> <span class="p">}</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="the-vector">The Vector</h3>

<p>Next, the idea of  <code class="language-plaintext highlighter-rouge">Knife</code> would be helpful as a tool for transferring condiments from jars to bread.  I initially designed this class with a <code class="language-plaintext highlighter-rouge">#load_from</code> method that could open the condiment jar if necessary (as in my rough example for Andrew), but on read-through now, it’s a bit concerning that a knife could know how to do that by itself.<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">6</a></sup> So, we’ll need to come back to a knife-operator class later on and build in some expertise there.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Knife</span>
  <span class="k">class</span> <span class="nc">KnifeDirtyError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>

  <span class="nb">attr_accessor</span> <span class="ss">:contents</span>

  <span class="k">def</span> <span class="nf">initialize</span>
    <span class="vi">@contents</span> <span class="o">=</span> <span class="kp">nil</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">clean?</span>
    <span class="n">contents</span><span class="p">.</span><span class="nf">nil?</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">clean!</span>
    <span class="n">contents</span> <span class="o">=</span> <span class="kp">nil</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">loaded?</span>
    <span class="o">!</span><span class="n">clean?</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">load_from!</span><span class="p">(</span><span class="n">container</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">loaded?</span>
      <span class="k">raise</span> <span class="no">KnifeDirtyError</span><span class="p">,</span> <span class="s2">"This knife is already loaded.  Don't mix your condiments!"</span>
    <span class="k">end</span>

    <span class="vi">@contents</span> <span class="o">=</span> <span class="n">container</span><span class="p">.</span><span class="nf">relinquish_condiment!</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="the-medium">The Medium</h3>

<p>Okay.  Bread.  As we’ve seen from the video, bread is tricky: conventionally, it’s got two condiment-accepting surfaces<sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote" rel="footnote">7</a></sup>  and a tasty, crusty, edge that should <em>not</em> accept condiments<sup id="fnref:8" role="doc-noteref"><a href="#fn:8" class="footnote" rel="footnote">8</a></sup>.  So, I’m going create a <code class="language-plaintext highlighter-rouge">SliceOfBread</code> class and manage its surfaces with the namespaced <code class="language-plaintext highlighter-rouge">SliceOfBread::Surface</code>.  After all, we may eventually want to do fancy triple-decker club-sandwiches and so on.  And the more over-engineered our code, the tastier the sandwich will be.  Right…?</p>

<p>The critical method here is <code class="language-plaintext highlighter-rouge">#smear!</code>, which will need to be provided with a loaded knife and one of the surfaces of the slice in question.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">SliceOfBread</span>
  <span class="k">class</span> <span class="nc">Surface</span>
    <span class="nb">attr_accessor</span> <span class="ss">:contents</span>

    <span class="k">def</span> <span class="nf">initialize</span>
      <span class="vi">@contents</span> <span class="o">=</span> <span class="kp">nil</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">plain?</span>
      <span class="n">contents</span><span class="p">.</span><span class="nf">nil?</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">smeared?</span>
      <span class="o">!</span><span class="n">plain?</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="k">class</span> <span class="nc">SurfaceAlreadySmearedError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>
  <span class="k">class</span> <span class="nc">KnifeNotLoadedError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>
  <span class="k">class</span> <span class="nc">InvalidKnifeError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>
  <span class="k">class</span> <span class="nc">InvalidSurfaceError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>

  <span class="nb">attr_reader</span> <span class="ss">:top</span>
  <span class="nb">attr_reader</span> <span class="ss">:bottom</span>

  <span class="k">def</span> <span class="nf">initialize</span>
    <span class="vi">@top</span> <span class="o">=</span> <span class="no">Surface</span><span class="p">.</span><span class="nf">new</span>
    <span class="vi">@bottom</span> <span class="o">=</span> <span class="no">Surface</span><span class="p">.</span><span class="nf">new</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">plain?</span>
    <span class="n">top</span><span class="p">.</span><span class="nf">plain?</span> <span class="n">and</span> <span class="n">bottom</span><span class="p">.</span><span class="nf">plain?</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">smeared?</span>
    <span class="n">top</span><span class="p">.</span><span class="nf">smeared?</span> <span class="n">or</span> <span class="n">bottom</span><span class="p">.</span><span class="nf">smeared?</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">smear!</span><span class="p">(</span><span class="n">knife</span><span class="p">:,</span> <span class="n">surface</span><span class="p">:)</span>
    <span class="k">unless</span> <span class="n">knife</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">Knife</span><span class="p">)</span>
      <span class="k">raise</span> <span class="no">InvalidKnifeError</span><span class="p">,</span> <span class="s2">"That's not hygienic."</span>
    <span class="k">end</span>

    <span class="k">unless</span> <span class="p">[</span><span class="ss">:top</span><span class="p">,</span> <span class="ss">:bottom</span><span class="p">].</span><span class="nf">include?</span><span class="p">(</span><span class="n">surface</span><span class="p">)</span>
      <span class="k">raise</span> <span class="no">InvalidSurfaceError</span><span class="p">,</span> <span class="s2">"What're you, crazy?  Put that knife away."</span>
    <span class="k">end</span>

    <span class="n">actual_surface</span> <span class="o">=</span> <span class="k">case</span> <span class="n">surface</span>
    <span class="k">when</span> <span class="ss">:top</span>
      <span class="n">top</span>
    <span class="k">when</span> <span class="ss">:bottom</span>
      <span class="n">bottom</span>
    <span class="k">end</span>

    <span class="k">unless</span> <span class="n">actual_surface</span><span class="p">.</span><span class="nf">plain?</span>
      <span class="k">raise</span> <span class="no">SurfaceAlreadySmearedError</span><span class="p">,</span> <span class="s2">"This surface was already smeared!"</span>
    <span class="k">end</span>

    <span class="k">unless</span> <span class="n">knife</span><span class="p">.</span><span class="nf">loaded?</span>
      <span class="k">raise</span> <span class="no">KnifeNotLoadedError</span><span class="p">,</span> <span class="s2">"This knife is too clean to smear with."</span>
    <span class="k">end</span>

    <span class="n">actual_surface</span><span class="p">.</span><span class="nf">contents</span> <span class="o">=</span> <span class="n">knife</span><span class="p">.</span><span class="nf">contents</span>
    <span class="n">knife</span><span class="p">.</span><span class="nf">contents</span> <span class="o">=</span> <span class="kp">nil</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="sandwichness">Sandwichness</h3>

<p>We can smear bread!  But what <em>is</em> a sandwich?  I posit, it’s at least two slices of bread stacked vertically on their non-crusty sides, provided that at least one of the inside surfaces is smeared and both of the outside surfaces are plain<sup id="fnref:9" role="doc-noteref"><a href="#fn:9" class="footnote" rel="footnote">9</a></sup>.  And, for good measure, cut in half on the non-crusty side with a clean knife.</p>

<p><code class="language-plaintext highlighter-rouge">Sandwich#build!</code> is the crucial method here (as it is in the video) and operates essentially as a series of checks to ensure that it is indeed permissible (according to the laws of the universe) to declare the smeared, carefully-ordered stack bread, indeed, a sandwich.  Failure at any stage results in the usual process-stopping exception with an appropriate name and helpful message.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Sandwich</span>
  <span class="k">class</span> <span class="nc">NotEnoughSlicesError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>
  <span class="k">class</span> <span class="nc">OutsideSmearedError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>
  <span class="k">class</span> <span class="nc">AlreadyBuiltError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>
  <span class="k">class</span> <span class="nc">TooPlainError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>
  <span class="k">class</span> <span class="nc">ImmatureSandwichError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>
  <span class="k">class</span> <span class="nc">AlreadyCutError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>

  <span class="c1"># These errors already exist in the Knife and SliceOfBread namespaces</span>
  <span class="c1"># respectively; I feel okay about adding them again here because I might</span>
  <span class="c1"># want to add specific code to these versions in the future.</span>
  <span class="k">class</span> <span class="nc">InvalidKnifeError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>
  <span class="k">class</span> <span class="nc">DirtyKnifeError</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>

  <span class="nb">attr_reader</span> <span class="ss">:slices</span>

  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="o">*</span><span class="n">slices_of_bread</span><span class="p">)</span>
    <span class="vi">@slices</span> <span class="o">=</span> <span class="n">slices_of_bread</span> <span class="o">||</span> <span class="p">[</span> <span class="p">]</span>
    <span class="vi">@built</span> <span class="o">=</span> <span class="kp">false</span>
    <span class="vi">@cut</span> <span class="o">=</span> <span class="kp">false</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">flavours</span>
    <span class="n">slices</span><span class="p">.</span><span class="nf">flat_map</span> <span class="k">do</span> <span class="o">|</span><span class="n">slice</span><span class="o">|</span>
      <span class="p">[</span> <span class="n">slice</span><span class="p">.</span><span class="nf">bottom</span><span class="p">.</span><span class="nf">contents</span><span class="p">,</span> <span class="n">slice</span><span class="p">.</span><span class="nf">top</span><span class="p">.</span><span class="nf">contents</span> <span class="p">]</span>
    <span class="k">end</span><span class="p">.</span><span class="nf">uniq</span><span class="p">.</span><span class="nf">compact</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">flavours_human_readable</span>
    <span class="n">f</span> <span class="o">=</span> <span class="n">flavours</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:downcase</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">f</span><span class="p">.</span><span class="nf">count</span> <span class="o">==</span> <span class="mi">2</span>
      <span class="k">return</span> <span class="n">f</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s1">' and '</span><span class="p">)</span>
    <span class="k">end</span>

    <span class="n">f</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'and '</span> <span class="o">+</span> <span class="n">f</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
    <span class="n">f</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s1">', '</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">&lt;&lt;</span><span class="p">(</span><span class="n">slice</span><span class="p">)</span>
    <span class="vi">@slices</span> <span class="o">&lt;&lt;</span> <span class="n">slice</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">ready_to_eat?</span>
    <span class="vi">@built</span> <span class="n">and</span> <span class="vi">@cut</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">build!</span>
    <span class="k">if</span> <span class="vi">@built</span>
      <span class="k">raise</span> <span class="no">AlreadyBuiltError</span><span class="p">,</span> <span class="s2">"It's already a glorious tower of food!"</span>
    <span class="k">end</span>

    <span class="k">if</span> <span class="n">slices</span><span class="p">.</span><span class="nf">count</span> <span class="o">&lt;</span> <span class="mi">2</span>
      <span class="k">raise</span> <span class="no">NotEnoughSlicesError</span><span class="p">,</span> <span class="s2">"</span><span class="si">#{</span><span class="n">slices</span><span class="p">.</span><span class="nf">length</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">slices</span><span class="p">.</span><span class="nf">length</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">?</span> <span class="s1">'slice'</span> <span class="p">:</span> <span class="s1">'slices'</span><span class="si">}</span><span class="s2"> of bread does not a sandwich make."</span>
    <span class="k">end</span>

    <span class="k">unless</span> <span class="n">slices</span><span class="p">.</span><span class="nf">first</span><span class="p">.</span><span class="nf">bottom</span><span class="p">.</span><span class="nf">plain?</span> <span class="n">and</span> <span class="n">slices</span><span class="p">.</span><span class="nf">last</span><span class="p">.</span><span class="nf">top</span><span class="p">.</span><span class="nf">plain?</span>
      <span class="k">raise</span> <span class="no">OutsideSmearedError</span><span class="p">,</span> <span class="s2">"This sandwich would be icky to hold."</span>
    <span class="k">end</span>

    <span class="c1"># Check all but the top slice for plainness</span>
    <span class="k">if</span> <span class="n">slices</span><span class="p">[</span><span class="o">..-</span><span class="mi">2</span><span class="p">].</span><span class="nf">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:plain?</span><span class="p">).</span><span class="nf">any?</span><span class="p">(</span><span class="kp">true</span><span class="p">)</span>
      <span class="k">raise</span> <span class="no">TooPlainError</span><span class="p">,</span> <span class="s2">"This sandwich might actually be a loaf."</span>
    <span class="k">end</span>

    <span class="vi">@built</span> <span class="o">=</span> <span class="kp">true</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">cut!</span><span class="p">(</span><span class="n">knife</span><span class="p">)</span>
    <span class="k">unless</span> <span class="vi">@built</span>
      <span class="k">raise</span> <span class="no">ImmatureSandwichError</span><span class="p">,</span> <span class="s2">"Build the sandwich and then cut it in one glorious stroke."</span>
    <span class="k">end</span>

    <span class="k">unless</span> <span class="n">knife</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">Knife</span><span class="p">)</span>
      <span class="k">raise</span> <span class="no">InvalidKnifeError</span><span class="p">,</span> <span class="s2">"That's not hygienic."</span>
    <span class="k">end</span>

    <span class="k">unless</span> <span class="n">knife</span><span class="p">.</span><span class="nf">clean?</span>
      <span class="k">raise</span> <span class="no">DirtyKnifeError</span><span class="p">,</span> <span class="s2">"No!  You'll get the edge all yucky with that knife."</span>
    <span class="k">end</span>

    <span class="k">if</span> <span class="vi">@cut</span>
      <span class="k">raise</span> <span class="no">AlreadyCutError</span><span class="p">,</span> <span class="s2">"One cut will do."</span>
    <span class="k">end</span>

    <span class="vi">@cut</span> <span class="o">=</span> <span class="kp">true</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>There.  We’ve got the components, tools, and definition of a standard sandwich with the associated methods necessary to go from ingredients and tools to a finished, verifiable product.  Sandwiches have meaning and instantiability in our universe.</p>

<h3 id="the-problem-unsolved">The Problem Unsolved</h3>

<p>But, without an intelligence to orchestrate a sandwich, to ensure the ingredients and tools and procedures are valid and sane, there are a <em>lot</em> of mistakes to make and a lot of snarky comments to encounter.  As seen in the video, sandwiches can fail.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">bread</span> <span class="o">=</span> <span class="mi">5</span><span class="p">.</span><span class="nf">times</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="no">SliceOfBread</span><span class="p">.</span><span class="nf">new</span> <span class="p">}</span>
<span class="n">pb</span> <span class="o">=</span> <span class="no">CondimentJar</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"Peanut Butter"</span><span class="p">)</span>
<span class="n">knife</span> <span class="o">=</span> <span class="no">Knife</span><span class="p">.</span><span class="nf">new</span>

<span class="n">knife</span><span class="p">.</span><span class="nf">load_from!</span><span class="p">(</span><span class="n">pb</span><span class="p">)</span>

<span class="c1">#=&gt; The jar is closed and knife-impermeable. (CondimentJar::ContainerClosedError)</span>

<span class="n">pb</span><span class="p">.</span><span class="nf">open!</span>

<span class="n">bread</span><span class="p">.</span><span class="nf">first</span><span class="p">.</span><span class="nf">smear!</span><span class="p">(</span>
  <span class="ss">surface: :top</span><span class="p">,</span>
  <span class="ss">knife: </span><span class="s2">"a shoe, why not?"</span>
<span class="p">)</span>

<span class="c1">#=&gt; That's not hygienic. (SliceOfBread::InvalidKnifeError)</span>

<span class="c1"># ... And so on.</span>
</code></pre></div></div>

<p>What we need is intelligence.  What we need is <em>Dad</em>.</p>

<p class="read-more"><a href="/blog/2022-12-19-a-simple-sandwich-ii/" class="heading flip-title">Part II.</a></p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>On a mature platform, anyway.  If the language or framework or silicon or whatever is buggy, then it <em>must</em> be the computer’s fault.  QED.  Shut up.  I’m perfect. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>That is, he can perform the requisite unscrewing motion on a closed container to open it without needing to be told explicitly how. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>Let alone ambiguously mutating them in the process.  Gross. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>I know Ruby methods do implicit returns.  But it seemed polite to be explicit with the knife.  Handle first, etc. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p>Somehow. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:6" role="doc-endnote">
      <p>What if it wanted to <code class="language-plaintext highlighter-rouge">#load_from</code> something other than a condiment?  Someone?  I can only write so many gate conditions. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:7" role="doc-endnote">
      <p>I won’t be subclassing <code class="language-plaintext highlighter-rouge">SliceOfBread::Heel</code> today. <a href="#fnref:7" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:8" role="doc-endnote">
      <p>No oil-drizzled focaccia. <a href="#fnref:8" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
    <li id="fn:9" role="doc-endnote">
      <p>At least, according to the requisite, <a href="https://en.wikipedia.org/wiki/John_Montagu,_4th_Earl_of_Sandwich">apocryphally-card-playing earl</a>. <a href="#fnref:9" class="reversefootnote" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Danny Fekete</name><email>danny@neckdeep.dev</email></author><category term="blog" /><category term="education" /><category term="programming" /><category term="executive function" /><summary type="html"><![CDATA[A deeply silly exploration of sandwich-building instructions in Ruby, inspired by assumptions we make about abstraction and some amusing cruelty to children.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://neckdeep.dev/assets/blog/sandwich/img/sandwich-universe.png" /><media:content medium="image" url="https://neckdeep.dev/assets/blog/sandwich/img/sandwich-universe.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>