Aquileo | Herman SchaafMostly software, books and lists of three thingsZola2026-07-31T00:00:00+00:00https://hermanschaaf.com/atom.xmlAquileo | Sky Lobby2026-06-04T00:00:00+00:002026-06-04T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/sky-lobby/<p>Sky Lobby is a game where you design elevator routes through a busy skyscraper. Place stops, build sky lobbies, and keep people moving as each tower becomes more crowded, more complex, and less forgiving. If you like games like Mini Metro or Mini Motorways, you should like this as well.</p> <p>I'm proud of releasing my first game and think it turned out quite well. If you want to show support and try it out, that would be amazing!</p> <p><em>Elevator dings</em></p> <h2 id="links">Links</h2> <ul> <li><a href="https://apps.apple.com/app/sky-lobby/id6763303024">Sky Lobby on App Store (iPhone + iPad)</a></li> <li><a href="https://play.google.com/store/apps/details?id=com.moyalabs.sky_lobby">Sky Lobby on Google Play (Android)</a></li> </ul> <figure style="margin: 1.5em 0; text-align: center;"> <div style="position: relative; width: 100%; margin: 0 auto; aspect-ratio: 16 / 9;"> <iframe src="https://www.youtube.com/embed/jMgV8pBgS1A" title="Sky Lobby trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border-radius: 4px;"></iframe> </div> <figcaption style="font-size: 0.85em; color: #777; margin-top: 0.5em; font-style: italic;">Sky Lobby trailer</figcaption> </figure> Aquileo | What we lost when we stopped programming2026-05-24T00:00:00+00:002026-05-24T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/what-we-lost-when-we-stopped-programming/<p>Today I want to share something quite personal, and more emotional than I would typically post on this blog. I have been living in a mild state of malaise, and for me, it took deep self-reflection to realize what the root cause was. Some time in the last year, slowly but surely, the world lost something important to me: the art of programming.</p> <p>I started programming when I was about 11 years old. It captured me pretty much instantly. It was just so much fun to be able to make games, interactive desktop wallpapers, and websites. I loved being able to make interactive experiences that I could share with others. I devoured every book about programming I could find, to the point where my parents got mildly concerned. And eventually I fell in love with the more theoretical side of programming as well: algorithms, data structures, and how to design and structure code so it's as clean and maintainable as possible. This led to competitive programming competitions, where I loved the thrill of participating in both local and global contests and putting my skills to the test against others. All the rounds of Google Code Jam, Hash Code, and other contests were yearly events I marked on my calendar in eager anticipation. And of course, programming became my career. An activity I greatly enjoyed and was lucky enough to be paid well for being good at it.</p> <p>And then came AI. Powerful models are now capable of writing code faster than any human ever could. They know every algorithm that's ever been invented. They solve most problems you throw at them. Those programming contests I used to mark on my calendar? Most of them don't exist anymore. A key reason is that if you were to now copy-paste the question into ChatGPT, you're very likely to get a working answer within seconds. Previously, even the fastest humans couldn't produce it in under 5 minutes, and the more difficult challenges took hours. And our dayjobs have transformed for the same reason: nobody can write code as fast as Claude or Codex can.</p> <p>On the one hand, AI is allowing us to create new software at previously unimaginable speeds. Shouldn't I be deriving my joy from that? I fell in love with programming because it allowed me to make things I could show others. I can still do that, and now even faster, right? In a sense, yes. And that aspect really is great. I love it.</p> <p>On the other hand, it cuts deeper. A whole class of things I used to spend most of my life on now feel pointless. Why bother learning Rust / Zig / Haskell if I won't be the one writing it? Why bother knowing anything at all? After all, I know I'll just keep prompting until the AI figures it out, even if sometimes it will have to Google something along the way. In the end something I used to pride myself in knowing and being good at (writing quick code that solves a problem or produces something amazing) is now something a robot can do much better and faster in 99% of cases.</p> <p>I still think I can do that 1% better, for now at least, such as architecting the system and designing the code to be as minimal and maintainable as possible. I can also understand and clean up what the robot builds, whereas a non-programmer couldn't. Arguably I am also more likely to think of the greater system and all its potential failure modes, and make sure the AI is thinking about it and testing this too. But the value of this is difficult to demonstrate to non-programmers. Heck, it can even be difficult to demonstrate to programmers. It takes a lot of time working with a codebase before you understand whether it is well-architected or not. As long as the code isn't a complete mess, you first need to understand the domain well enough to see whether any complexity in the code is warranted, or whether it is inherent to the domain. It can take days or weeks to do this, in a brave new world where we are getting used to working in timescales of minutes and hours.</p> <p>For a long time, I attributed my mild malaise to the general uncertainty these technological advancements bring for the future, in tandem with large political changes we are currently seeing across the world. While that definitely factors in as well, for me personally, that is only half of it. I have lost the joy in practicing the art of programming. Coding a large system over hours, days or weeks, taking a step back, and being genuinely proud of my work, is a feeling I may rarely get to enjoy again. Not when you can, at least from the outsider's point of view, achieve the same result within hours with AI. We all know the end result is not really identical, but as it turns out, the economic incentives just aren't aligned to doing it the slow, thoughtful, artisanal way. Like a carpenter being replaced by machines and assembly lines, if I want to create some artisinal hand-crafted code now, it would either have to be on my own time, or I need to be able to set myself apart through what I produce in a way that no AI could. And even then, to stretch the carpenter analogy a bit more, I would be wasting time if I didn't at least use some power tools.</p> <p>This is quite a big change, and one that, it turns out, I'm still getting to grips with. Am I in the fourth stage of grief, and the next stage will be acceptance? Perhaps. Time will tell.</p> <figure style="margin: 1.5em 0; text-align: center;"> <img src="/images/hermes.webp"/> <figcaption style="font-size: 0.85em; color: #777; margin-top: 0.5em; font-style: italic;">A scene from The Six Million Dollar Mon</figcaption> </figure> <p>In an episode of Futurama called "The Six Million Dollar Mon", Hermes Conrad gets replaced by a robot called Mark 7G. In a futile attempt to stay relevant, Hermes starts replacing more and more of his body with robotic components. Until finally the only part he hasn't replaced is his brain. He then decides to replace the last bit of humanity left in him, and get a robot brain as well. It remains to be seen whether we as programmers will be fully replaced by robots, like Hermes with Mark 7G, or merely use AI as power tool-like augmentation of our existing skills to stay competitive.</p> <p>Personally, I'm more in the power tools camp. These new tools are exciting, giving us superpowers to build even greater things, and as participants in the process we can still be proud of the result. I'm sure, in time, we'll learn to wield them with great skill. However, it was important for me to reflect on what we have lost along the way. For some, like me, well-designed code can be a beautiful thing. Artisanal, hand-crafted software will still have its place, but choosing that path will be an artist's life from now on.</p> Aquileo | My Mental Model for “Is It Worth Automating?” Has Changed2026-05-17T00:00:00+00:002026-05-17T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/the-is-it-worth-automating-mental-model-has-changed/<p>So, to set the backdrop: It's May 2026, and we're all talking about AI, all the time. Well, some of us, anyway. And there was an inflection point in the abilities of coding agents around January 2026, to the point where they are now good enough to be trusted with writing a lot of code. This could be good or bad, depending on your view, and <a href="https://x.com/mitchellh/status/2055380239711457578">maybe you shouldn't trust AI with your entire codebase yet</a>.</p> <p>Something I haven't seen discussed much is just how useful AI can be <em>outside</em> of your main codebase: we can let AI write code to automate things that previously would have had a <em>terrible</em> work-to-time ratios. Obligatory XKCD:</p> <p><a href="https://imgs.xkcd.com/comics/automation_2x.png" target="_blank" rel="noopener"><img src="https://imgs.xkcd.com/comics/automation_2x.png" alt="Original Automation XKCD comic" style="max-width: 60%; height: auto;"/></a></p> <p>The above XKCD comic was from January 2014. Now, in 2026, the "reality" graph may very well look like the "theory" graph from before.</p> <p>To illustrate, I'd like to highlight some examples from a game project I've been working on. There were many instances where I asked AI to create automation for problems where I previously would not even have considered automation at all.</p> <h1 id="automation-gone-wild">Automation gone wild</h1> <p>During development of a solo dev game, Sky Lobby, I tasked AI with automating the following, among other things:</p> <h2 id="1-bots-to-playtest-the-game-simulating-different-player-styles-and-skill-levels">1. Bots to playtest the game, simulating different player styles and skill levels</h2> <p>Sky Lobby is a puzzle game that involves both strategy and reflexes. Before real playtesting started, I wanted to get a quick feel for how difficult every level was, without spending hours playing the full game myself (however fun that may have been... time is a solo developer's most precious resource!). So I asked AI to write a bot that played the game.</p> <p>It was to implement and compare four strategies: <code>passive</code>, <code>random</code>, a <code>sweep</code> that brute-forces a floor-by-floor tour, and a <code>heuristic</code> that actually plays well. Each strategy ran repeatedly across multiple reflex tiers, generating win rates and engagement flags per level. As the bots play at a sped-up rate, it takes about three minutes to finish the simulations, and then I get a markdown report showing which levels are too easy (the simple strategies win), too hard (no strategies win), or too boring (long periods of not needing to do anything). This was useful for getting an initial baseline for all the levels, and it allowed me to fine-tune them before playing through them myself and later sending them to human players.</p> <p><strong>Cost to automate with AI</strong>: 15 minutes</p> <p><strong>Time saved</strong>: Days, either of manual testing, or developing similar bots</p> <p>Previously I may also have written such bot playtesters, but it would have taken a few days, at which point it probably would have been faster and better to play the game through a couple of times.</p> <p>In addition to the markdown file, I wanted to see how the bots played, to make sure they weren't getting stuck or doing something silly. So AI wrote a shell script that drives Godot headless with <code>--write-movie</code>, records each level, and concatenates them into one playthrough video. In addition to giving me further ideas for refining the game and bot behavior, this was great for getting a feel for what it is like to play the game from start to finish, condensed into a few minutes.</p> <p><strong>Cost to automate with AI</strong>: 5 minutes</p> <p><strong>Time saved</strong>: Hours, maybe days</p> <p>Would I have done this at all, before? Probably not.</p> <h2 id="2-a-script-for-syncing-game-center-entries">2. A script for syncing Game Center entries</h2> <p>Game Center provisioning means 11 leaderboards, 2 global achievements, and per-level 3-star achievements, each in 10 localizations with its own icon. The official tool is App Store Connect's web UI. I spent 10 minutes manually entering data until I thought, "I'm spending a lot of time on this task...". I asked AI to write code that hits the App Store Connect REST API directly, reads every localization from a CSV, and provisions it all idempotently. This is one of those things that I, as a solo dev working on a small project and only expecting to do this task once or twice, probably would not have automated before, but since it's so easy to automate now, it suddenly made sense and ended up saving me a lot of time.</p> <p><strong>Cost to automate with AI</strong>: 10 minutes</p> <p><strong>Time saved</strong>: 2-3 hours of manual entry, or 2-3 hours of writing a similar script by hand</p> <h2 id="3-screenshots-of-the-game-for-every-ios-simulator">3. Screenshots of the game for every iOS simulator</h2> <p>Early beta testers reported empty areas on the top and bottom of the screen that I had trouble reproducing on my own iPhone. I asked AI to write a script that takes screenshots of the game being played on every possible device. It came up with a bash script that picks from available simulators, installs the build, launches it with a <code>--shoot-gameplay</code> flag I added, waits six seconds, and grabs a screenshot per device via <code>simctl</code>. The whole sweep takes a couple of minutes.</p> <figure style="margin: 1.5em 0; text-align: center;"> <a href="/images/sky-lobby/devices_2rows.png" target="_blank" rel="noopener"><img src="/images/sky-lobby/devices_2rows.png" alt="A grid of Sky Lobby screenshots taken across twelve different iOS simulators, from iPhone SE to iPad Pro 13-inch" style="max-width: 100%; height: auto; border-radius: 4px;"/></a> <figcaption style="font-size: 0.85em; color: #777; margin-top: 0.5em; font-style: italic;">The same Sky Lobby level captured across a dozen iOS simulators in a single run: from the tiny iPhone SE up to the iPad Pro 13" M5. A few layout bugs stood out immediately.</figcaption> </figure> <p><strong>Cost to automate with AI</strong>: 10 minutes</p> <p><strong>Time saved</strong>: At least a few hours of me figuring out how to write such a script myself, or hours of me testing manually.</p> <p>The quality of the game benefited from this AI-built automation, without AI changing any of the game code directly.</p> <h2 id="4-a-promo-video-as-code">4. A promo video as code</h2> <p>Instead of editing a trailer in a video editor, the game promo is a GDScript file that drives a live tower simulation through a 17-second storyboard. Doors open, humans walk on, the tower builds, the camera follows, a title card lands. I had a vision of what I wanted the trailer to be like, I collected the assets I wanted to use for it, and I let AI program the specifics. It's not going to win any Oscars, but it worked, and allowed me to spend more time focusing on the game itself.</p> <figure style="margin: 1.5em 0; text-align: center;"> <div style="position: relative; width: 100%; max-width: 315px; margin: 0 auto; aspect-ratio: 9 / 16;"> <iframe src="https://www.youtube.com/embed/kur4Pesk2SE" title="Sky Lobby trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border-radius: 4px;"></iframe> </div> <figcaption style="font-size: 0.85em; color: #777; margin-top: 0.5em; font-style: italic;">The resulting trailer, rendered straight out of Godot from a GDScript storyboard.</figcaption> </figure> <p>That said, this one did take a lot of iterations to get right. I went back to ask the AI to tweak it somewhere between 50 and 100 times before I was (more or less) satisfied with the result. Early on I tried editing the code myself, but it quickly became clear that this was not going to happen: the code was one 1,700-line ball of mud, relying on hundreds of inscrutable functions and constants.</p> <p><strong>Cost to automate with AI</strong>: A few hours, spread over a few days</p> <p><strong>Time saved</strong>: Hard to say. Maybe a few hours?</p> <p>As an aside: one nice consequence of using Godot for this was that when I changed the cabs or added details to the buildings, the trailer automatically picked them up in the next render. It's always showing the current build. Which is how Godot's <code>--write-movie</code> is intended to be used, I guess, but it was new to me, and being able to let AI write the promo was much more successful than my earlier attempts at recreating the game's feel in external Lottie software.</p> <h1 id="what-this-means">What this means</h1> <p>Now, AI saved a lot of time here, and that's not so surprising. But the main thing I want to point out about these examples is that <em>most of these are throw-away scripts</em>; code that lives outside the main codebase. It doesn't really matter if it's poorly architected or not maintainable, as long as it does the job the one or two times I need it. This sidesteps one of the common objections against AI coding tools, which is that they are not (yet) able to build clean, well-architected systems. Plus, if AI didn't write these, probably no one would have, because it just didn't make sense before.</p> <h1 id="on-the-other-hand">On the other hand</h1> <p>Still, some objections remain. And they are clearer when viewed through the lens of the examples we went through above. Here are a few:</p> <ol> <li><strong>You miss out on learning opportunities</strong>. I now don't know how to automate iOS screenshots, or the intricacies of converting avi to mp4. I'm still not able to use Lottie animation software properly. I do have some AI-written examples to learn from if I <em>did</em> ever need to learn these skills, but they tend to be inscrutable. Which brings me to the next point,</li> <li><strong>You become reliant on AI</strong>. When I tried to edit the code for the promo video, I simply... couldn't. Sure, given a few hours and maybe some questions to the agent about how it all works, I could've gotten there. But at the point it would probably have been better to write the code from scratch myself. With the sunk cost fallacy firmly forgotten, I kept prompting away for hours to get it to match my original vision. AI becomes the only lense through which you can understand the code.</li> <li><strong>It doesn't consistently save time</strong>. In some of these examples, the time savings were obvious and immediate. In the promo video case, I'm not so sure. I guess what I take away from this is that, since it's quite easy and fast to try, it's worth asking the AI to see if it works, but when you get 5-6 prompts in and it's still not looking good, then perhaps it's time to suck it up and do it yourself.</li> </ol> <h1 id="updating-our-mental-models">Updating our mental models</h1> <p>The <em>Automation</em> XKCD comic was released in 2014, and it has, despite obvious exaggeration for humorous effect, been part of my mental model about what is worth automating and what is not ever since. The threshold for “worth automating” has collapsed, and this mental model now needs to be updated.</p> <p>A script that once required half a day of context-switching, setup, and debugging can now often be produced in minutes. For the first time, the "Theory" part of the comic really does match reality (or can, at least).</p> <p>An old, invisible category of software will now experience a quiet growth: disposable automation. Small, messy, highly specific tools that exist only to solve one problem and then quietly disappear. Historically, many of these tasks stayed manual because the overhead of writing the automation outweighed the benefit.</p> <p>There's a parallel here with MCP servers and OpenClaw, where you let AI connect to services to perform mundane tasks for you. The small difference is that you're asking AI to write code, executing that code only once, or a few times, and then throwing it away. This may also be one of the safest and most effective uses of AI coding tools right now. Throw-away scripts do not need perfect architecture, long-term maintainability, or elegant abstractions. They just need to work well enough to save time.</p> <hr> <h1 id="about-the-game">About the game</h1> <p>Thanks for reading. If you're interested, the project mentioned in this post is a mobile game called <a href="https://www.moyalabs.com/sky-lobby/">Sky Lobby</a>. You design and dispatch elevators in ever-growing skyscrapers. You need to place stops, build sky lobbies, and keep people moving as each tower becomes more crowded, more complex, and less forgiving. It's pretty fun, and I had a lot of fun developing it.</p> Aquileo | A Free Chinese Dictionary, and Why I Built One2026-04-16T00:00:00+00:002026-04-16T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/moya-chinese-dictionary/<p>A few weeks ago I <a href="/announcing-moya-chinese">launched the Moya Chinese iOS app</a>, the spaced-repetition Chinese flashcards app that, eleven years after my first attempt, finally seems to be working for me. I'm on a 50-day streak, and I can barely believe it! Since the initial launch, I've released several more versions with lots of improvements, including listening and character writing modes and an integrated dictionary.</p> <p>And today I'm launching its companion: a free web dictionary at <a href="https://www.moyachinese.com/">moyachinese.com</a>.</p> <p><a href="https://www.moyachinese.com/"><img src="/images/moya-chinese-dictionary/moya-chinese-screenshot.png" alt="Moya Chinese Dictionary Screenshot" /></a></p> <p>If you're excited about the idea and just want to try it, great, please go ahead! Or if you want to know why a perfectly good world full of perfectly good Chinese dictionaries needed yet another one, read on.</p> <h3 id="yet-another-chinese-dictionary">Yet Another Chinese Dictionary?</h3> <p>I love a good Chinese dictionary. MDBG, Pleco, LINE Dict — I've used them all, and they're all wonderful in different ways. So I want to be upfront about it: the Moya Chinese dictionary is standing on the shoulders of giants, and I owe a lot to MDBG in particular.</p> <p>But here is what Moya Chinese does differently:</p> <ul> <li><strong>Clean design</strong>. As much as I've loved using MDBG over the years, its design really felt like it was leaving a lot to be desired, and I don't think it's been updated in the 15 years I've been using it. With the Moya Chinese dictionary I wanted to address this, first and foremost.</li> <li><strong>Fine-tuned search algorithm</strong>. It's hard to create a dictionary search that works the way users expect, and often with other dictionaries I couldn't find what I was looking for. So I put a lot of thought into how to do this for Moya. By combining factors like how common the word is, its HSK level, and how well it matches the search term, I think I've landed on something that works well most of the time.</li> <li><strong>120,000+ words</strong>, with pinyin, English, and a breakdown of each character's components.</li> <li><strong>Search in Chinese, pinyin, or English.</strong> No need to switch input methods to look something up.</li> <li><strong>Paste a whole sentence</strong> and it will break the sentence down into words and translate each. Useful when you're squinting at a Chinese subtitle and the dictionary lookup is happening in your head one syllable at a time.</li> <li><strong>Example sentences</strong> for every word in the HSK and TV/Movie frequency lists (sourced from Tatoeba)</li> <li><strong>Interface in English, French, Spanish, Japanese, or Korean.</strong> Same as the iOS app.</li> <li><strong>Free, no ads, no account.</strong> I just wanted to provide something useful to the community. Bookmark it, and use it when you want to look up a word.</li> </ul> <p>That's roughly it. The design goal was the same as for the iOS app: useful, clean, no nonsense. And the plan is to (soon) integrate the two, so that when you look up a word in the dictionary, you can easily add it to your flashcard study queue.</p> <h3 id="the-long-tail-revisited">The Long Tail, Revisited</h3> <p>In my <a href="/announcing-moya-chinese">post about the iOS app</a>, I related how I built a website called <em>ChineseLevel</em> on the premise that knowing the top 1,000 most common words would let you understand 95% of a newspaper. This is a tempting and very wrong idea, and I now believe it has done some damage to a lot of beginners (sorry).</p> <p>It's tempting because the underlying math really does work. The first ~590 Chinese characters cover about 80% of running text, ~940 cover 90%, and ~2,400 cover 99% (<a href="https://en.wikipedia.org/wiki/Chinese_character_frequency">Chinese character frequency, Wikipedia</a>). You can see the curve flatten out quite dramatically:</p> <p><img src="/images/moya-chinese-dictionary/character-coverage.svg" alt="Cumulative coverage of Chinese text by the N most-common characters, showing the long tail" /></p> <p>The shape of that curve is roughly Zipfian, the same distribution that shows up in word frequencies in pretty much every language ever measured (<a href="https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0010729">SUBTLEX-CH, Cai &amp; Brysbaert, PLOS ONE 2010</a>). And it really is true that if you know the top 940 characters, you'll <em>recognise</em> 9 out of every 10 characters on a page.</p> <p>The problem is that the one in ten you don't recognise is, almost without fail, the character that carries the actual information. The rare word "indictment" matters more to a news article than the next thousand instances of "the". A linguistic version of the <a href="https://en.wikipedia.org/wiki/Pareto_principle">Pareto principle</a> seems to apply here: a small fraction of the words do most of the work, but a different small fraction of the words carries most of the meaning.</p> <p>This is why a 120,000-word long-tail dictionary is, in my opinion, exactly the kind of dictionary a learner needs by their side. The first 2,000 words you can get from your textbook. It's the next 100,000 that decide whether you can read a novel or follow a TV series.</p> <h3 id="the-hsk-that-was-and-the-hsk-that-will-be">The HSK That Was, and the HSK That Will Be</h3> <p>If you're learning Mandarin in any structured way, sooner or later you're going to bump into the <strong>HSK</strong> (汉语水平考试), the standard Chinese proficiency test. The current syllabus, HSK 2.0, has six levels and tops out at around 5,000 words. The new HSK 3.0 standard, <a href="https://www.mandarinzone.com/new-hsk-test/">published in November 2025 and effective from July 2026</a>, keeps the lower levels but extends the ladder all the way to <strong>11,000 words</strong> across nine levels:</p> <p><img src="/images/moya-chinese-dictionary/hsk-vocabulary.svg" alt="Cumulative HSK vocabulary by level, comparing HSK 2.0 to HSK 3.0" /></p> <p>That's more than doubling the official upper bound, and the new levels 7–9 are designed to take learners well into the long tail. I think this is great.</p> <p>But even the new HSK doesn't quite plug all the gaps. A recent post on the <a href="https://www.moyachinese.com/blog/">Moya Chinese blog</a>, <a href="https://www.moyachinese.com/blog/the-words-hsk-forgot/"><strong>The Words HSK Forgot</strong></a>, is about exactly this: I crunched subtitle frequency data against the HSK lists and found that roughly half of the most common words in Chinese media don't appear in HSK at all. Words like 女孩 (girl), 男孩 (boy), 加油 (the universal cheer), 之前 (before), 之后 (after) — all conspicuously absent. The HSK and the subtitle corpus simply have different goals: one optimises for cultural breadth and exam-fairness, the other reflects what people actually say to each other. HSK 3.0 closes the gap a lot — top-1,000-word coverage jumps from 48% to 76% — but a gap remains.</p> <h3 id="robots-buddies-and-other-joyful-compounds">Robots, Buddies, and Other Joyful Compounds</h3> <p>Long-tail vocabulary is also where Chinese is at its most fun. The other post I want to point you to is <a href="https://www.moyachinese.com/blog/robot-in-chinese/"><strong>Machine Person: All the Ways to Say "Robot" in Chinese</strong></a>. The standard mainland word for "robot" is 机器人 <em>jīqìrén</em>, literally <strong>machine person</strong>. In Hong Kong, it's 机械人 <em>jīxièrén</em>, <strong>mechanical person</strong>. A humanoid robot is a 人形机器人, a <em>human-shaped machine person</em>, which is delightful.</p> <p>English borrowed "robot" from the Czech <em>robota</em> (forced labour), via Karel Čapek's 1920 play <em>R.U.R.</em> (<a href="https://en.wikipedia.org/wiki/Robot">Wikipedia: Robot</a>). Chinese, characteristically, just describes the thing. The same instinct gives us 飞机 (flying machine = airplane), 手机 (hand machine = mobile phone), and ancient automatons called 木人 (wooden person). Once you start seeing the pattern, every new word feels a bit like solving a tiny <a href="/cryptic-crosswords-and-how-to-solve-them">cryptic crossword clue</a>.</p> <p>There are a few more posts up already, including one on <a href="https://www.moyachinese.com/blog/spaced-repetition-chinese/">why spaced repetition works for Chinese</a> and one introducing <a href="https://www.moyachinese.com/blog/word-snake-a-new-way-to-grow-your-chinese-vocabulary/">Word Snake</a>, a small puzzle game where you chain HSK words across an 8×8 grid of characters. I'm planning to write more along these lines — short, opinionated, hopefully useful, with a leaning towards the kind of vocabulary the textbooks skipped.</p> <h3 id="try-it">Try It</h3> <p>The dictionary is at <a href="https://www.moyachinese.com/"><strong>moyachinese.com</strong></a>. The iOS app is on the <a href="https://apps.apple.com/app/moya-chinese-flashcards/id6759454887">App Store</a>. Both are free. If you give either of them a try, I'd genuinely love to hear what you think: what works, what doesn't, what words you wish were there. Send me a note, and I'll keep tinkering.</p> <p>再见 zàijiàn!</p> Aquileo | Moya Chinese2026-03-03T00:00:00+00:002026-03-03T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/announcing-moya-chinese/<p>Today, I'm super happy to launch my <a href="https://www.moyalabs.com/chinese">new iOS app for learning Chinese</a>, 11 years in the making. But before I say more, let me tell you the story of how I got here.</p> <p>As an English-speaker, Mandarin has a steep learning curve, with its reliance on tones and thousands of characters. My Mandarin-learning journey started 17 years ago, and it was slow-going initially. But as time went on, and I got over the initial hump, things started clicking. At least the grammar is quite simple really, without many irregularities, and once you remember the hundred or so basic components of characters, they become easier to learn as well.</p> <p>Of course, traveling to China and living in Taiwan for many years also helped a lot; there's nothing like constant immersion to get the brain into gear. But eventually my time there came to an end, and since then I've found it hard to keep the muscle practiced.</p> <p>So 11 years ago, over a weekend, I built a simple spaced-repetition flashcard app as an initial experiment with React Native. It became, as far as I know, the first React Native app to be published on the iOS app store, outside of Facebook. I wrote a very excited technical <a href="https://hermanschaaf.com/building-a-flashcard-app-with-react-native/">blog post</a> about the experience at the time. The post was well-received by the programming community, and a few intrepid Chinese learners even paid the $0.99 to try it out. I was thrilled!</p> <p>But it didn't help my Chinese very much, I'm afraid. The first version of the app only had relatively simple words. It failed to form a learning habit for me, I never developed it further, and eventually I stopped learning Chinese altogether. But I've always been fascinated by language learning and acquisition, and in the intervening years I have accumulated many thoughts about it. One, which is backed by a lot of science, is that <strong>consistency is king</strong>. It's far better, in terms of long-term retention, to learn a little bit every day than it is to cram the same amount in a short period of time. Another is that <strong>long-tail vocabulary is actually really important</strong>. Let me explain.</p> <p>Another website I developed, a few years before the React Native flashcard app, was called ChineseLevel. It operated on the idea that if you knew the first 1,000 words in order of the frequency that they appear in texts, then you should be able to understand, say, 95% of a newspaper. With 2,000 words, 99%, and so on. So I wrote a little online test you could do to check your level, and then recommended newspaper articles of that approximate complexity, as far as possible. It worked, and proved fairly popular actually.</p> <p>But it had a fatal flaw: that idea is wrong. Why? Well, it IS true that you would only need to look up maybe 1% of the words in the article, so that is useful, but it is NOT true that you would actually understand 99% of what is written without looking anything up. In fact, you will probably understand around 0%. That's because all the critical information you need for understanding is very likely to be contained in exactly that one word you don't know. In fact, the rarer the word is, the more information it carries! So while learning words in the order of frequency is a reasonable approach, I guess, learning long-tail vocabulary is actually really important for any kind of understanding in real-world situations.</p> <p>That brings us to the present day. After many years of living in England and not practicing Chinese at all, I wanted to start improving it again, but I wasn't willing to relocate my family to Asia (my kids probably wouldn't have been very thrilled either). So, next best thing, I wanted an app that combined those two insights: consistency, and long-tail vocabulary, so that I can build a habit of learning new words. I've tried flashcard apps like Anki before, and often I would spend a few hours building decks, but then run out of steam actually learning them after a few days. It's especially demotivating if you miss a few days, then come back to a huge backlog of words that would take hours to go through. Often my habit stopped right there.</p> <p>And that's where <a href="https://www.moyalabs.com/chinese"><strong>Moya Chinese</strong></a> came from. It's a Chinese-learning app for learners that have at least basic familiarity with Chinese, and it helps them build their vocabulary further. It is best paired with some other form of immersion, like classroom learning, videos and movies, or actually visiting places where Mandarin is spoken. For me, that's Chinese TV series—they can be really good!</p> <p><a href="https://apps.apple.com/app/moya-chinese-flashcards/id6759454887" title="Moya Chinese Flashcards App Store Listing"><img alt="Moya Chinese Flashcards App Store Share Card" style="max-width:100%; float: left; margin-bottom: 1rem;" src="/images/moya-chinese-share-card.png"></a></p> <p>To make habit-forming natural, the app has some very minimal gamification. You start with a bamboo seedling, and every day you reach your goal, it grows a tiny bit. After a few months of consistent learning, it will grow into a huge, thriving bamboo plant: a visual metaphor for the growing you have been doing yourself. And to add a teeny bit of excitement, the app also rewards you with random items along your journey that you can use to decorate the room around the plant. These gameplay elements are very minimal on purpose: I don't want to waste your time, and the app is focused on learning. It won't have ads, and I want users to only spend a few minutes a day, but every day. Consistency is king.</p> <p>I also worked out a new spaced repetition algorithm for the app so that you don't get the Anki problem of backlog fatigue. I won't go into the details here, but it's meant to better balance the competing goals of resurfacing words just as you're about to forget them and habit-forming with a constant amount of time spent every day. I relied on the HSK system for determining your approximate level and building relevant vocabulary from there.</p> <p>If you know anyone learning Mandarin, I'd be very grateful if you told them about Moya Chinese and encouraged them to try it and send me some feedback. I'm actively looking to improve it further, the app even ships with a way to vote on what the next features should be. (You can also <a href="https://www.moyalabs.com/chinese">vote here</a>.) It's free to download on the Apple App Store. (Sorry Android users: no Android support yet, but you can vote for it on the roadmap).</p> <p>In any case, even if I remain the only person to ever use this app, I'd still be happy. Throughout the beta-testing period I've built solid recall for hundreds of new words, and it's exciting to recognize them in the series we watch, and know my comprehension is improving. And I'm just as excited to see where this will lead. Thanks for reading, 再见 zàijiàn!</p> Aquileo | New Game: Picnic on the Moon2026-02-07T00:00:00+00:002026-02-07T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/new-game-picnic-on-the-moon/<p>I've added a new game to the site: <a href="/picnic-on-the-moon">Picnic on the Moon</a>. 🚀 It's a daily puzzle game where you need to figure out the hidden connection between the items that a robot is bringing on a picnic. Try to solve it in as few guesses as possible, and check back every day for a new puzzle!</p> <p>This is part of a new section of the site called "<a href="/games">Fun &amp; Games</a>". The other section there, also added this week, is the <a href="/spelling-bee">Spelling Bee Analysis</a>, a more data-driven look at the New York Times Spelling Bee, inspired by the blog post about solving every Spelling Bee puzzle, <a href="/every-nyt-spelling-bee-puzzle-solved/">now and forever</a>.</p> <p>I'm planning to add some more experimental puzzle games in the coming months. Claude Code is making it easier than ever to try new ideas and see what's fun. It's exciting to finally get a chance to materialize some of the ideas for puzzle games I've had floating in my head around for years! Stay tuned for more, and let me know if you have any feedback or ideas for fun puzzles you'd like to see.</p> Aquileo | Should Makers Still Care About Code Quality?2026-01-31T00:00:00+00:002026-01-31T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/should-makers-care-about-code-quality/<p>For a long time now, it's been possible to divide the software world into two camps: those who see code as a craft, and those who see code as a means to an end. For the purposes of this post, let's call them <strong>Coders</strong> and <strong>Makers</strong>.</p> <h3 id="coders-vs-makers">Coders vs Makers</h3> <p>At the one extreme, Coders believe that code should be clean, as described in books like Robert C. Martin's (a.k.a. Uncle Bob's) <a href="https://www.amazon.co.uk/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882">Clean Code</a> or Steve McConnell's <a href="https://www.amazon.co.uk/Code-Complete-Practical-Handbook-Construction/dp/0735619670">Code Complete</a>. At the micro-level, this means variable, class, and function names should be self-documenting, comments should make intentions clear, and code re-use should be the default. At the macro-level, components should have clear separation of concerns, clean interfaces, and code should be thoroughly tested at every level.</p> <p>At the other extreme, Makers pride themselves on shipping fast. They believe that clean code doesn't matter; what matters is that it works. Ship fast, ship often, worry about the code quality later (if ever). Software requirements are inherently poorly-defined and malleable, so rather than waste time building the wrong thing perfectly, build it imperfectly, get user feedback, and iterate towards the solution that users really want. If you don't, the Makers reason, you will be left in the dust by your competitors who do.</p> <p>Now, I've caricatured these viewpoints, and there are many possible shades in between. In my view, the "best" viewpoint is the one that says "it depends" and adapts its approach according to context. And even though I enjoy writing code and like it to be clean as much as the next person, I just as often find myself siding with the Maker point of view in business settings, especially in startups. Of course, if you're developing software for serious use cases, like banking or healthcare, then the risks are greater and quality matters much more. But on the other hand, if you're developing a prototype to see if there's market fit for a SaaS idea, then arguably moving fast matters more. Still, Coders and Makers often struggle to see eye to eye, and product discussions can get heated when one engineer believes they should refactor the codebase, leading to better developer experience, and the other would rather ship the next feature, leading to better user experience.</p> <h3 id="and-then-came-the-agents">…And Then Came the Agents</h3> <p>Okay. But this is a decades-old debate at this point. Why am I bringing this up now?</p> <p>With <a href="https://en.wikipedia.org/wiki/Vibe_coding">Vibe coding</a> becoming a thing, I think this debate is more important now than ever. It is becoming increasingly possible to leave coding entirely to LLM-powered agents such as Claude Code. And many of my programmer friends say they are doing exactly that: they hardly write a line of code themselves anymore. Their jobs now are to write the prompts that produce the code. Ideally they would review the code and correct the agent if it is going down the wrong path… but do you have the patience to carefully review thousands of lines of code with changes across a codebase, generated within a few minutes of Claude <em>pontificating</em> and <em>reticulating</em>? I don't think most of us do. So we tend to read the summary of what the LLM tells us it did, or is planning to do, and if it sounds good, we let it implement the code. And we believe we're increasing our productivity, because there's no way we could have written that much ourselves in such a short amount of time.</p> <p>Taken in isolation, usually most code written by the LLM looks reasonable. Every pull request LGTM, so to speak. At the micro-level, LLMs mostly follow the good practice guidelines loved by Coders: well-named variables and functions, comments making intentions clear. But zoom out one level, and quickly you find that at the macro-level, things are a mess. Code re-use? It's more likely the LLM has defined two functions in different parts of the codebase that do the same thing, although implemented subtly differently. Clear separation of concerns? Unlikely, but maybe if you're lucky. Tests? Hopefully, if you asked for them. But do the tests have sensible assertions, or do they just run the code? Or maybe they check every minutia of the underlying code in hundreds of fine-grained test cases, losing the bigger picture. You better check. Dead or unreachable code? All over the place.</p> <p>Perhaps the problem lies with me: did I provide a detailed enough specification of what needs to be done? Indeed, I could spend time writing several pages of clear design specifications, and then have the LLM implement it in one shot. Done from scratch on a new codebase, I think this may turn out okay. But when let loose on an existing codebase, my experience has been that the LLM's lack of full visibility of the big picture quickly leads to a mess. At this point in time, LLM-based systems are highly capable of handling simple tasks like basic algorithms. However, when presented with large projects over hundreds of files, they struggle to maintain order.</p> <p>When recently developing the first version of <a href="https://www.bankstatement.to">a bank statement converter</a>, I wrote almost all the code by hand, with some help from GitHub Copilot or ChatGPT here and there for completing small sections. One HackerNews observer memorably compared this approach to <a href="https://news.ycombinator.com/item?id=46703091">"banging rocks together"</a> when compared to more agentic approaches. But I carefully desiged the interfaces, components, and individual units to a level of polish that I was satisfied with. And, to me, the codebase was relatively clean, well-tested and easy to modify. But, fearing I need to speed things up, I let Claude Code inspect the codebase and asked it to implement a new feature. Within 15 minutes it had disabled CSRF protections (to get rid of those pesky "Forbidden" errors!) and implemented a dozen new functions, hundreds of lines each, that subtly recreated already-existing functionality for user authentication but with minor modifications. It all worked, but changes it made to authentication were only applied to its new endpoints, not the existing code, so users could run into errors depending on which sequence of pages they visited. Clearly, this relationship was not meant to last, and I soon went back to my old rock-banging methods.</p> <p>Yet from the Maker's point of view, what happened was all fine, right? The application after Claude's modifications did what it's supposed to. After all, I didn't explicitly <em>ask</em> it to keep CSRF protections intact, or to ensure existing endpoints worked consistently with the new functionality. And when I asked it to do so, it tried tirelessly until it had spent all my credits, and probably would have succeeded eventually (but never quite did, by the way).</p> <p>So we're moving fast and breaking things, as Mark Zuckerberg (in)famously put it. But with LLMs, I fear we may be taking the "breaking things" part a bit too far. There is a hidden cost to messy code: development velocity decreases, because every change introduces a new bug or inconsistency somewhere else. We also risk serious security vulnerabilities. It's not just Claude's disdain for CSRF. In March 2025, Matt Palmer, an engineer at Replit, wrote a report finding that 170 out of 1,645 Lovable-created web apps were suffering from a security flaw, easily allowing hackers to extract highly sensitive information. (It should be mentioned here that Replit is a competitor of Lovable, but the vulnerability pointed out, it seems, was indeed real.)</p> <p>So even the Makers, at some point, will be forced to reconcile the reality that while they were able to move incredibly fast initially, eventually the untidiness of the approach started slowing them down, perhaps to the point where they have to start looking at the code themselves, or turn to someone with the experience who can. And this is where the wisdom of "it depends" becomes so important. Maybe the fast approach was fine for the MVP (Minimum Viable Product) or MLP (Minimum Lovable Product, perhaps the more appropriate term in this setting), but be prepared to either start over or pay down that technical debt in the future, should your application gain some traction.</p> <h3 id="so-what-can-we-do">So what can we do?</h3> <p>Clearly, LLMs are extremely useful tools. And Vibe coding, still in its infancy today, very well may replace traditional "artisanal" coding in the long run as AI systems continue to improve. But we also can't (or at least, shouldn't) simply let them loose on our codebases today.</p> <p>So here's an idea: <strong>what if we developed a way to measure the "cleanliness" of a codebase, both at the micro- and macro-levels, and then let the AI Agents tirelessly optimize, like only they can, until the codebase is certified as 100% "clean"</strong>? We can fight fire with fire and let the agents tidy up their own spaghetti code. Sure, it will cost us some more tokens, but it will work, right? …Right? Well, maybe.</p> <p>While it definitely can help to provide AI assistants with more tools: linters, code coverage, maybe even cyclomatic complexity, I think the problem you'll run into with this is that all these tools tend to operate at the micro-level. Taking a step back to the macro-level, it becomes increasingly difficult to tell whether code is complex because the domain it's operating in is inherently complex, or the code is complex because it was poorly designed. This distinction has also been called <a href="https://www.iankduncan.com/engineering/2025-05-26-when-is-complexity-accidental">Accidental or Essential Complexity</a>.</p> <p>But perhaps this issue, too, is not insurmountable. If we define clean code (at the macro level) as code that is easy to modify in new directions safely and correctly, then perhaps we <em>can</em> measure the <em>cleanliness</em> of a codebase. Well-designed code should be easy to understand, verify and modify, or at least as easy as the underlying domain allows. That has always been the case, and it is still true for coding agents.</p> <p>So what if we created a measure that said, essentially, for a given set of changes in functionality, how many lines, functions and files need to be modified to implement it, including tests? We could have an LLM generate 100 hypothetical changes in functionality that a codebase might require in the future, implement each one, and then measure the average of this number. We could even, somehow, express the number as a percentage of the size of the codebase. Then we can optimize to get this number as close as possible to 0% (though obviously never quite reach it), and either accept or reject alternative architectural designs for the codebase based on whether this number is lower or higher.</p> <p>I don't know. Maybe that could work. But the computational cost (not to mention the token cost) would make such an approach prohibitively expensive today.</p> <p>I think there may be some simpler tricks worth trying first, like perhaps giving LLMs a means to "visualize" the graph of components in a codebase, and asking it to ensure the dependency graph at various levels (files, classes, functions) remains as tree-like as possible, with no cycles. And we can highlight to it, using vectorized representations, spots where code duplication seems to be high, and ask it to refactor while maintaining the tree structure and keeping the interfaces between components as clean as possible and ensure all the tests continue to pass. I'm keen to try this, and would be interested to know if others have had success with such ideas already. Maybe some MCP tools for this already exist.</p> <h3 id="conclusion">Conclusion</h3> <p>To answer the question in the title of the post, in case it needed answering: Yes, Makers should care about code quality. And likewise, Coders should care about real-world impact. But as to how much they should care… <em>it depends</em>. And sometimes maybe they don't need to care at all. The goal and context of a project should always be taken into account.</p> <p>With LLMs now writing so much new code, it's tempting to believe that code quality is going to matter less and less, because, after all, that will be the LLM's problem to deal with now. But low code quality can have knock-on effects with real-world impact down the line, whether it's slower velocity, bugs or security vulnerabilities. And even if extreme Makers don't care about code quality directly, they should at least care about those things.</p> <p>My experience with this has been much the same as described in Mo Bitar's blog post, <a href="https://atmoio.substack.com/p/after-two-years-of-vibecoding-im">After two years of vibecoding, I'm back to writing by hand</a>. From where we stand today, it seems like LLMs just cannot deliver high quality at the macro-level yet. Given the right tools, or context far beyond what they are capable of today, maybe one day they will, and I'm keen to see how we can push the boundaries of this problem. I laid out some ideas in this post that I'll be keen to try in the near term. But for now, for anything even vaguely important, I'll continue being the architect of my code and only let the LLMs sweat the small stuff that I can easily verify, thank you very much.</p> Aquileo | Cryptic Crosswords: What They Are and How to Solve Them2026-01-30T00:00:00+00:002026-01-30T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/cryptic-crosswords-and-how-to-solve-them/<p>I still remember the first time I tried doing a cryptic crossword. The clues seemed completely impenetrable.</p> <blockquote> <p><strong>Duck as light motorcycle loses one wheel? (6)</strong></p> <p><strong>Resentful air spoiled farewell observances (7,5)</strong></p> <p><strong>Highest standards of ladies' dancing (6)</strong></p> </blockquote> <p>What? 😂</p> <p>Accepting defeat without completing even a single answer, I decided to look up the answers <a href="#footnotes"><sup>*</sup></a>. Maybe I'm just stupid?</p> <blockquote> <p><strong>Duck as light motorcycle loses one wheel? (6)</strong>: SCOTER</p> <p><strong>Resentful air spoiled farewell observances (7,5)</strong>: FUNERALRITES</p> <p><strong>Highest standards of ladies' dancing (6)</strong> IDEALS</p> </blockquote> <p>What!? 😂😂😂</p> <p>Clearly, these were not the crosswords my mother taught me.</p> <p>I sat down and tried to make sense of this madness. Eventually, after some trial and error, it clicked. And you know what? Now I actually love solving these. Maybe exactly because they seemed so, well, cryptic, before?</p> <p>Don't get me wrong: they are still very, very difficult. But no longer impossible! And if, through this post, I can help you take the same step from impossible to merely improbable, then I would have succeeded.</p> <hr /> <h2 id="the-principle">The Principle</h2> <p>Cryptic crosswords follow a simple principle once you know it: <strong>every clue has two paths to the same answer</strong>. There’s the definition, which works like a standard crossword clue, and the wordplay, which gives a coded instruction to assemble the answer from letters, abbreviations, or rearrangements. Both are present in every clue, and both lead to the same solution.</p> <hr /> <h2 id="spotting-the-definition">Spotting the Definition</h2> <p>Definitions usually sit at the beginning or end of a clue.</p> <p>Example:</p> <blockquote> <p><em>Duck as light motorcycle loses one wheel? (6)</em></p> </blockquote> <ul> <li><strong>Definition</strong>: "Duck" (a kind of bird)</li> <li><strong>Wordplay</strong>: "light motorcycle loses one wheel": SCOOTER → drop one O → SCOTER</li> <li><strong>Answer</strong>: <em>SCOTER</em> (a <a href="https://en.wikipedia.org/wiki/Common_scoter">Common Scoter</a> is a kind of duck)</li> </ul> <p>Both the definition and the wordplay lead to the same answer, but from different angles.</p> <hr /> <h2 id="wordplay-indicators">Wordplay Indicators</h2> <p>Indicators are the puzzle’s instructions in disguise. Words like <em>wild</em>, <em>holding</em>, <em>returning</em>, or <em>reportedly</em> signal an operation to perform on letters. Recognizing this turns cryptic crosswords from random words into a kind of language that you can learn to decode.</p> <p>Common types:</p> <ul> <li><strong>Anagrams</strong>: e.g. "mixed," "confused," "wild"</li> <li><strong>Containers</strong>: e.g. "inside," "around," "holding"</li> <li><strong>Deletions</strong>: e.g. "without," "losing," "missing"</li> <li><strong>Reversals</strong>: e.g. "back," "returning," "reversed"</li> <li><strong>Homophones</strong>: e.g. "heard," "aloud," "reportedly"</li> <li><strong>Palindromes</strong>: e.g. "back and forth"</li> </ul> <hr /> <h2 id="examples-of-wordplay">Examples of Wordplay</h2> <h3 id="anagrams">Anagrams</h3> <blockquote> <p><em>Highest standards of ladies' dancing (6)</em></p> </blockquote> <ul> <li>"Dancing" indicates an anagram</li> <li>Rearrange <em>ladies</em> → <strong>IDEALS</strong></li> <li>Definition: "Highest standards"</li> </ul> <blockquote> <p><em>Resentful air spoiled farewell observances (7,5)</em></p> </blockquote> <ul> <li>"Spoiled" indicates an anagram</li> <li>Rearrange <em>Resentful air</em> → <strong>FUNERALRITES</strong></li> <li>Definition: "Farewell observances"</li> </ul> <blockquote> <p><em>Intuit a chess blunder, making up for it (12)</em></p> </blockquote> <ul> <li>"Blunder" indicates an anagram</li> <li>Rearrange <em>Intuit a chess</em> → <strong>ENTHUSIASTIC</strong></li> <li>Definition: "Making up for it" (this definition seems a bit of a stretch to me, maybe I'm missing something)</li> </ul> <h3 id="containers">Containers</h3> <blockquote> <p><em>Don't be cruel to old comedian, simple at heart (2,4,2)</em></p> </blockquote> <ul> <li>"Simple at heart" -&gt; <strong>EASY</strong> as the middle word</li> <li>"Old comedian" -&gt; <strong>GOON</strong>, from <a href="https://en.wikipedia.org/wiki/The_Goon_Show">The Goon Show</a></li> <li>"Don't be cruel to" -&gt; <strong>GO EASY ON</strong></li> </ul> <p><strong>EASY</strong> needs to be placed inside <strong>GOON</strong>, leading to <strong>GO EASY ON</strong>.</p> <h3 id="charades">Charades</h3> <p>Charades are a combination of synonyms or abbreviations.</p> <blockquote> <p><em>Qualify to get drink for ID (8)</em></p> </blockquote> <ul> <li>"Qualify" = <strong>PASS</strong></li> <li>"Drink" = <strong>PORT</strong></li> <li>"ID" = <strong>PASSPORT</strong> (<strong>PASS</strong> + <strong>PORT</strong>)</li> </ul> <h3 id="deletions">Deletions</h3> <blockquote> <p><em>Duck as light motorcycle loses one wheel? (6)</em></p> </blockquote> <ul> <li>"duck" = <strong>SCOTER</strong></li> <li>"light motorcycle" = <strong>SCOOTER</strong> → loses one wheel → <strong>SCOTER</strong></li> </ul> <h3 id="double-definitions">Double Definitions</h3> <p>Sometimes the answer simply comes from two different definitions. Here we need to look for overlap.</p> <blockquote> <p><em>The Great Escape - film and book (6)</em></p> </blockquote> <ul> <li>"Escape" = "EXODUS" → <a href="https://en.wikipedia.org/wiki/Exodus_(1960_film)">1960s film</a></li> <li>"Escape" = "EXODUS" → Book in the Bible</li> </ul> <p>Another example:</p> <blockquote> <p><em>Doubt the gospel? (8,4)</em></p> </blockquote> <ul> <li>Doubt the gospel: <strong>QUESTION MARK</strong> - Mark being a book in the New Testament.</li> <li>Doubt: <strong>QUESTION MARK</strong> - the "?" symbol.</li> </ul> <h3 id="palindromes">Palindromes</h3> <p>There's some hint that the answer reads the same backwards.</p> <blockquote> <p><em>Holy woman going back and forth (3)</em> "Holy woman" = <strong>NUN</strong> - it reads the same back and forth</p> </blockquote> <hr /> <h2 id="how-i-approach-a-clue-now">How I Approach a Clue Now</h2> <ol> <li>Look at the letter count in the enumeration</li> <li>Identify the likely definition (start or end)</li> <li>Find indicator words</li> <li>Break the rest into wordplay units</li> <li>Use crossing letters to confirm or adjust</li> </ol> <hr /> <h2 id="why-cryptic-crosswords">Why Cryptic Crosswords</h2> <p>Cryptic crosswords are not easy. But they start making sense when you realize that every clue is a small, self-contained puzzle. The sense of order hidden in the chaos is addictive. The first few puzzles felt impenetrable, but once I started understanding the conventions, solving them felt like cracking a code rather than guessing at random words.</p> <p>If you are willing to spend a few hours learning the patterns, cryptic crosswords open a new world of challenge and satisfaction. Start small, track the indicators, and have a scratchpad handy.</p> <p>Cryptic crosswords can be frustrating and rewarding in equal measure. Have fun!</p> <div class="footnote" id="footnotes"> <p><strong>Notes</strong></p> <p>Most of the examples in this post are from <a href="https://www.theguardian.com/crosswords/cryptic/29916">The Guardian's Cryptic crossword No 29,916</a>.</p> <p>If you're keen to try a cryptic crossword after reading this (as you should be!), <a href="https://www.theguardian.com/crosswords/series/cryptic">The Guardian's website</a> has a large archive of puzzles to try.</p> </div>Aquileo | Solving Every NYT Spelling Bee Puzzle, Now and Forever2025-01-05T00:00:00+00:002025-01-05T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/every-nyt-spelling-bee-puzzle-solved/<style> .hint { color: blue; text-decoration: underline; cursor: help; } .puzzle { background: #f0f0f0; padding: 1em; text-align: center; margin-bottom: 1em; } </style> <p>The <a href="https://www.nytimes.com/puzzles/spelling-bee">New York Times Spelling Bee</a> is a fun little puzzle game that asks you, given seven letters, to find as many words with those letters as you can.</p> <figure style="float: left; margin-right: 2em; max-width: 300px" > <img style="width: 100%" src="/images/spelling-bee/spelling-bee.png" title="Example of a past New York Times Spelling Bee game" alt="Example of a past New York Times Spelling Bee game"/> <figcaption>Example of a past New York Times Spelling Bee game</figcaption> </figure> <p>There are a few restrictions:</p> <ul> <li>the letter in the center must always be used</li> <li>other than that, letters can be used any number of times</li> <li>valid words must have least 4 letters</li> </ul> <p>You get a score based on the number and length of words you found. For each puzzle, you get a ranking based on the total number of words that are possible, with "Genius" being the highest. There is also a special "Queen Bee" crown for finding all the words in a particular day's puzzle.</p> <p>After playing this game for a while, I had many questions. How many unique games of Spelling Bee are there? Which combinations would have the most possible words? And which ones the fewest? What is the highest possible score? And can we solve all the puzzles programmatically? In this post, I will answer all these burning questions, and more.</p> <h2 id="bringing-all-the-bees-to-the-yard">Bringing all The Bees to the Yard</h2> <p>There are quite a few possible games, as it turns out. <strong>4,604,600</strong>, to be exact. That's enough to have a unique game every day for the next 12,000 years. So it's unlikely that the New York Times will need to start repeating puzzles any time soon.</p> <p>I wrote some code to first generate all the possible puzzles, then solve them.</p> <p>The straight-forward way to generate all the possible puzzles is to take the center letter as constant, then generate all combinations of six letters chosen from the remaining 25 letters in the alphabet. Repeat this for every center letter, and you have all the possible puzzles.</p> <p>Finding all valid solutions to a particular puzzle is also not difficult. The brute force method is to write some code that goes through the word list, and for every word check if it: 1. contains the center letter at least once, and 2. only consists of one of the seven valid letters. If so, add it to the list of valid words for that puzzle.</p> <p>The tricky part, however, is checking all 4,604,600 puzzles. I estimated how long the naive method would take on my laptop, and got a number measured in years. Since I didn't want to wait around that long, I made a few optimizations. The main one was to create 26 different word lists: one for every "must-have" center character. This cut the number of words we need to check down by approximately 96%. I also removed words of length 3 or less, which removed another percentage point or two. These changes, plus a few more Go-specific tweaks, got the total time down to around 45 minutes, just enough to grab some dinner. I wrote all the results to a local DuckDB database, so I could analyze them at my leisure later.</p> <h2 id="harvesting-the-honey">Harvesting the Honey</h2> <p>Boy, were the results interesting! The graph below summarizes the maximum, minimum and average number of words for the 26 possible center letters. You can click on the labels to see each one more clearly. Friendly advice: if you see a "Q" in the center of one of these puzzles, run.</p> <p><canvas id="spellingBeeChart1"></canvas></p> <p>There are a few interesting things to highlight from this graph. It's hard to believe, but based on the minimums, if the center letter is an A, then there will always be at least one word, no matter what the other letters are. Here's one such example. Can you find the single word that is to be found? (Center letter in <b>Bold</b>) (<span class="hint" data-hint="Another word for water (4 letters)">Hint</span>)</p> <div class="puzzle"><b>A</b>OQUVXZ</div> <p>Here's another one. This one actually has two valid words. (<span class="hint" data-hint="An informal word for coffee, and a music genre often involving a saxophone.">Hint</span>).</p> <div class="puzzle"><b>A</b>JOUVXZ</div> <p>On the other end of the spectrum, if Q is the center letter, then in the majority of cases there are no words to be found, at all. Here is one such impossible puzzle.:</p> <div class="puzzle"><b>Q</b>CGHJIU</div> <p>The puzzle with the most possible words has an E in the center. It weighs in at a whopping 1,390 possible words, and has a maximum score of 9,982. Here it is:</p> <div class="puzzle"><b>E</b>AINRST</div> <h3 id="a-dance-off-with-the-official-puzzles">A Dance-off with the Official Puzzles</h3> <p>If you often play Spelling Bee, you may have noticed that in the above analysis, we did not account for the fact that official puzzles always have <strong>at least one pangram</strong>. (That is, at least one word that uses all the letters.) So the New York Times will never publish some of the puzzles shown earlier, like the one with Q in the center, or even the ones with A. This restriction brings down the maximum number of puzzles as well.</p> <p>How many are there if we don't allow for puzzles that don't have at least one pangram? When taking the pangram rule into account, there are approximately 119,609 possible puzzles that the NY Times could publish. That's still 327 years worth of daily puzzles, but it means that quite soon, NYT will already have published 2% of all possible Spelling Bee puzzles!</p> <p><canvas id="spellingBeeChart2"></canvas></p> <p>As far as official puzzles are concerned, let's see what the most common center letters are. And has there ever been a puzzle with Q as the center letter? It turns out that yes, that happened on 17 November, 2023. But, surprisingly, two letters have <strong>never</strong> been the center of attention, at the time of writing: S and X:</p> <p><canvas id="spellingBeeChart3"></canvas></p> <p>This came as a big surprise to me, and I went back to check the data several times. But it's true! S and X have never appeared in the center of an official puzzle. Statistically speaking, it's about time—even J and Q have been in the spotlight at least once. New York Times editors, if you're reading this, here's a puzzle with both S and X, with X in the center, that will certainly get everyone talking. It has a maximum score of 538: high, but not <em>that</em> high. It even features 2 pangrams! (3 if you allow for a rare word.) (<span class="hint" data-hint="One pangram relates to geometry. The other you might find lacking.">Hint</span>)</p> <div class="puzzle"><b>X</b>AEILST</div> <p>So that's the history center letters in official puzzles. What about the frequency of letters featuring anywhere in previous official puzzles? Here's what that looks like:</p> <p><canvas id="spellingBeeChart4"></canvas></p> <p>Well, now I'm just getting upset! What do they have against the letter S? Is this some kind of conspiracy!? Even X has featured 109 times! In normal English text, the <a href="https://en.wikipedia.org/wiki/Letter_frequency">frequency of the letter S is 6.3%</a>. In dictionaries, it's 8.7%. My guess is that the NYT team thinks it would be boring to be able to add S to pluralize many of the words, but that often happens with other suffixes as well, like -d or -ed. I also found that this had been <a href="https://www.reddit.com/r/NYTSpellingBee/comments/v355om/no_spelling_bee_ever_includes_the_letter_s/">noticed by others</a> before. It's striking how similar the two graphs above look. It seems like they must drawing letters from the same distribution, one somehow different from normal English distributions, and never includes S. NYT team, if you're reading this, please consider using the letter S in a puzzle. If it helps, here is one with a maximum score of 438 and a great pangram. (<span class="hint" data-hint="The pangram starts with F and is the plural form of a word has two meanings: playing a particular instrument, or something petty.">Hint</span>)</p> <div class="puzzle"><b>S</b>DFGILN</div> <p>For a long time puzzles also did not feature ER or ING, but eventually it did happen. In fact, ING was featured in the puzzle on the day I was writing this! (January 5, 2025)</p> <h2 id="flight-of-the-four-million-bumble-bees">Flight of the Four Million Bumble Bees</h2> <p>Spelling Bee fans can rest easy: as long as you don't go to the lengths I went to, and write code to generate and solve all possible puzzles, you have another 327 years of daily puzzles to enjoy. But I leave you with one final puzzle: one the NYT will (probably) never publish, and perhaps my favorite. It has only a single valid word, its pangram. Can you find it, and crown yourself Queen Bee of the smallest future puzzle? (<span class="hint" data-hint="7 letters, starts with J">Hint</span>)</p> <div class="puzzle"><b>X</b>BEJKOU</div> <h2 id="notes">Notes</h2> <p>I didn't have access to the official word list used by the New York Times, so I used an <a href="https://github.com/wordnik/wordlist">open source word list for game developers</a>. The official Spelling Bee puzzles often remove words that the authors deem obscure or offensive. My analysis showed that they have, at the time of writing, accepted 10,468 unique words, and ruled out 10,517 from my word list as being official. This still left 94,387 words about which I couldn't be sure, so in this post I assumed that all words from the word list were accepted.</p> <p>Another interesting thing that came out of this analysis was that about 300 words were accepted in some official puzzles, but not in others! I suppose it's up to the puzzle author, or maybe they update their official word list over time. Anyway, I hope you had fun reading this. If you have further thoughts or insights, I'd love to chat about it! Feel free to reach out using the contact details below.</p> <h3 id="update-6-feb-2026">Update (6 Feb, 2026)</h3> <p>I recently went over this data again to see if anything changed. And it did! The letter S has finally made it to a puzzle, albeit not yet as the center letter. On 12th March, 2025 the puzzle was <strong>F</strong>ABLOSU (can you find the pangram? <span class="hint" data-hint="FABULOUS">Answer</span>), and on the 1st of September 2025 the puzzle was <strong>B</strong>CHKLOS (<span class="hint" data-hint="SCHOOLBOOK, SCHOOLBOOKS">2 Pangrams</span>).</p> <p>I've also added a new section to this site, <a href="/spelling-bee/">Daily Spelling Bee Hints and Analysis</a>, for daily stats that update automatically.</p> <h2 id="comments">Comments</h2> <hr> <p><i>On 11 April, 2025</i>, <strong>Sofia</strong> wrote in noting that since this article was originally published, NYT did actually publish a puzzle containing the letter S. Hooray! The pangram was FABULOUS, and the other letters were chosen such that there weren't too many plurals that could be formed with -S.</p> <p>Sofia was also interested in the fact that no puzzle containing both E and R has ever been published, and asked what this meant for the total number of possible puzzles. If we assume this to be a steadfast rule for official puzzles, the number of possible puzzles reduces from 119,609 to 85,729. That would mean that, at the time of writing, well over 3% of all the possible puzzles have already been published.</p> <p>Thanks for your delightful note and questions, Sofia!</p> <hr> <p><i>On 22 Nov, 2025</i>, <strong>Stephen</strong> wrote in asking what the longest possible Spelling Bee word would be, both for official and hypothetical puzzles. The longest he had come across was <em>nationalization</em> (15 letters).</p> <p>Well, Stephen, I'm happy to report that you were right! <em>nationalization</em> is the longest word that's ever appeared in an official puzzle, and it appeared on January 8, 2024. Looking forward to future puzzles, the maximum would be 17 letters for a word most people would know (disinterestedness), and 18 letters for a more obscure word (coccidioidomycosis). Here are the longest possible words I found:</p> <table><thead><tr><th>Count</th><th>Words</th></tr></thead><tbody> <tr><td><strong>18</strong></td><td>coccidioidomycosis</td></tr> <tr><td><strong>17</strong></td><td>disinterestedness</td></tr> <tr><td><strong>16</strong></td><td>antitotalitarian, associationistic, antinationalists, stereoisomerisms</td></tr> <tr><td><strong>15</strong></td><td>citriculturists, transparentness, sententiousness, snippersnappers, inoffensiveness, inconveniencing, unconsciousness, semiretirements, antinationalist, electroreceptor, suspensefulness, associationists, nonindependence, defenselessness, anticlimactical, ultrastructural, inattentiveness, nonintervention, inexpensiveness, interestingness, overengineering, interconnection, micrometeoritic, nontotalitarian, nonengineerings, insensitiveness, nationalization, stereoisomerism, remorselessness, contortionistic</td></tr> </tbody></table> <p>I removed some words ending in -esses, like <em>disinterestednesses</em> (19), as I think these are too rare to be accepted in official puzzles. <em>coccidioidomycosis</em> (18) may also be a bit rare, to be fair, and I think the real winner should be <em>disinterestedness</em> (17).</p> <p>Thanks for the great question Stephen!</p> <div style="clear: both"></div> <script src="/js/spelling-bee.js"></script>Aquileo | 3 Books for New Engineering Leaders2024-12-21T00:00:00+00:002024-12-21T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/books-for-new-engineering-leaders/<p>It's almost the end of 2024, making it a good time to reflect on the past year. The biggest change, for me, was taking on a new role as director of engineering at <a href="https://www.cloudquery.io">CloudQuery</a>. Coming from a staff software engineering role, then team lead, and now organizational lead, I suspected it would be a mistake to treat this new role as just another day as a software engineer. The skills I had built up on the Individual Contributor (IC) track wouldn't all translate to the new responsibilities that come with this role. And hang on, what even were my responsibilities now? I would need to do a lot of learning and growing, and not in the technical way of learning new technologies that I was used to. This was—I'll freely admit—scary. But it was also an exciting opportunity, and I decided to approach it as such.</p> <p>Over the last couple of months I have read as much as I can about engineering leadership, and in this post I wanted to share with you a few of the books I found most useful, with great advice I could put into practice almost immediately. Whether you are thinking about making the transition from IC to engineering manager, from engineering manager to director of engineering, from director to VP of engineering or CTO, or even if you are an experienced engineering leader, there are lots of valuable insights packed into all these books.</p> <h2 id="1-become-a-great-engineering-leader-build-effective-skills-to-lead-and-grow">1. <a href="https://pragprog.com/titles/jsenglb/become-a-great-engineering-leader/">Become a Great Engineering Leader</a> - Build Effective Skills to Lead and Grow</h2> <p><em>by James Stanier</em></p> <p> <a href="https:&#x2F;&#x2F;pragprog.com&#x2F;titles&#x2F;jsenglb&#x2F;become-a-great-engineering-leader&#x2F;"> <img src="/images/book-covers/become-a-great-engineering-leader.jpg" title="Book cover for Become a Great Engineering Leader" style="max-width: 240px; margin: 0 20px 10px 0; float: left;" /> </a> This book is excellent, and I was very lucky to have picked it up first. It is very pragmatic. It first explains how the different roles are defined ("VP, Director, What?") and the responsibilities that come with them. Then it moves on to practical tools and techniques for your day-to-day, and finally to strategy, planning and execution. I first read this in the weekend leading up to my first day in the role, and by following the advice, I often felt a few steps ahead, which was great.</p> <p>If you've been in an engineering leadership role for a long time and are doing well in it, perhaps most of the advice in this book will not be new to you, but there should still be some ideas you can apply. The book frequently references other popular business books and briefly summarizes the key ideas (books like <a href="https://teamtopologies.com/">Team Topologies</a> and <a href="https://www.amazon.co.uk/Finite-Infinite-Games-James-Carse/dp/1476731713">Finite and Infinite Games</a>), so it can also be a good entrypoint for further topics to explore.</p> <p>I still return to this book frequently.</p> <h2 id="2-scaling-people-tactics-for-management-and-company-building">2. <a href="https://press.stripe.com/scaling-people">Scaling People</a> - Tactics for Management and Company Building</h2> <p><em>by Claire Hughes Johnson</em></p> <p> <a href="https:&#x2F;&#x2F;press.stripe.com&#x2F;scaling-people"> <img src="/images/book-covers/scaling-people.jpg" title="Book cover for Scaling People" style="max-width: 240px; margin: 0 0 10px 20px; float: right;" /> </a> On a scale of pragmatism to idealism, if <em>Become a Great Engineering Leader</em> is all the way on the pragmatic side, <em>Scaling People</em> would be further towards the idealistic side. Perhaps this is not quite fair: <em>Scaling People</em> also comes with lots of practical, tactical advice. But the advice and wisdom is buried far deeper inside stories and anecdotes than is the case for Become a Great Engineering Leader. This made <em>Scaling People</em> a book that was a bit harder to get into, and one that is probably more worthwhile from a longer term perspective. It also comes with checklists and worksheets to help you on your journey.</p> <p>The author, Claire Hughes Johnson, is an accomplished leader of some of the biggest, most successful software teams. First at Google, then later at Stripe. This is probably also what made the book feel less pragmatic to me, someone at an earlier stage company. But there are still plenty of good lessons for leaders at all scales. I was able to apply a lot of the advice about hiring in the last few months.</p> <p>The last thing I'll say about this--something I didn't realize at the time of purchase--is that this book is published by Stripe Press. Yes, a publishing arm owned by the very same Stripe the author works at. The result is, unfortunately, that many sections of the book, however well-intentioned, read like sales material trying to convince the reader to join Stripe. I didn't count exactly, but roughly half the book is dedicated to sections laying out Stripe's values, documents and history.</p> <h2 id="3-how-big-things-get-done-the-surprising-factors-behind-every-successful-project-from-home-renovations-to-space-exploration">3. <a href="https://amazon.co.uk/How-Big-Things-Get-Done/dp/1035018934">How Big Things Get Done</a> - The Surprising Factors Behind Every Successful Project, from Home Renovations to Space Exploration</h2> <p><em>by Professor Bent Flyvbjerg, Dan Gardner</em></p> <p> <a href="https:&#x2F;&#x2F;amazon.co.uk&#x2F;How-Big-Things-Get-Done&#x2F;dp&#x2F;1035018934"> <img src="/images/book-covers/how-big-things-get-done.jpg" title="Book cover for How Big Things Get Done" style="max-width: 240px; margin: 0 20px 10px 0; float: left;" /> </a> By <a href="/how-i-choose-nonfiction-books/">my own criteria from a blog post 6 years ago</a>, I probably shouldn't have picked up this book, as it includes the word "surprising" on the cover. But I listened to this book in audio format, and so I didn't see its cover, and at least it did well on the other two criteria, by having generally great reviews and being written by an expert in the field. And it just goes to show, heuristics are just that: heuristics! I'm glad I picked it up.</p> <p>This book is not as directly applicable to engineering management as the previous two on the list, but my main take-away from it is in how to better estimate how long complex projects will take: a critical skill for any engineering manager who needs to negotiate and communicate this with other parts of the business.</p> <p>A very natural mistake we often make when estimating how long projects will take is to think about all the individual tasks that make up the project, estimate how long each of those should take, and then add them up to guess how long the project should take overall. If we have some experience in project estimation, maybe we'll add a fudge factor, like 2x, to account of unknowns, or try to account for <a href="https://en.wikipedia.org/wiki/Hofstadter%27s_law">Hofstadter's law</a>.</p> <p>But the authors argue, quite persuasively, that a better approach is to use what is called <a href="https://en.wikipedia.org/wiki/Reference_class_forecasting#:~:text=Reference%20class%20forecasting%20or%20comparison,Daniel%20Kahneman%20and%20Amos%20Tversky.">reference-class forecasting</a> (RCF), a framework initially developed by Daniel Kahneman and Amos Tversky (and won them the Nobel Prize in Economics, by the way). RFC is a fancy name for what is essentially the idea that you should look at previous projects of a similar class to estimate how long your project will take. You can then add or subtract some time based on any factors that make your project (or team) special or different. For example, if you are planning on adding customizable dashboards for your SaaS product, the best way to estimate how long that will take is to look at how long it took to add in other products, and then adjust from there. Now, this information is often hard to come by, and that's probably the fatal problem with this approach, but with some creativity, it can usually be done with some degree of accuracy, which is enough for decent forecasts.</p> <h2 id="honorable-mentions">Honorable Mentions</h2> <h3 id="supercommunicators"><a href="https://www.penguin.co.uk/books/444545/supercommunicators-by-duhigg-charles/9781847943644">Supercommunicators</a></h3> <p><em>by Charles Duhigg</em></p> <p>Supercommunicators explores the art and science of effective communication, revealing the strategies and techniques used by skilled conversationalists to navigate challenging topics, build connections, and guide decisions. Through real-world examples, the book uncovers how to turn difficult conversations into successful outcomes.</p> <h3 id="good-strategy-bad-strategy"><a href="https://www.amazon.co.uk/Good-Strategy-Bad-Difference-Matters/dp/1846684811">Good Strategy / Bad Strategy</a></h3> <p><em>by Richard Rumelt</em></p> <p>The main message is that good strategy stems from a clear understanding (or hypothesis) of what the biggest problem is to solve, and coming up with a set of actions that will solve this problem. Good strategy amounts to more than simply setting ambitious goals or thinking positively.</p> <h2 id="closing-thoughts">Closing Thoughts</h2> <p>There are many more books that could have made this list; books like <a href="https://www.amazon.co.uk/Elegant-Puzzle-Systems-Engineering-Management/dp/1732265186">An Elegant Puzzle</a>, <a href="https://www.amazon.co.uk/Manager%60s-Path-Camille-Fournier/dp/1491973897/261-0331686-3190254">The Manager's Path</a> and <a href="https://www.amazon.co.uk/Engineering-Executives-Primer-Impactful-Leadership/dp/1098149483/261-0331686-3190254">The Engineering Executive's Primer</a>, for example. I would also recommend these. At the end of the day, it's good to know where to start, but I believe it is just as important to read widely, both in categories directly applicable to your field and in those adjacent. Build a picture of the full spectrum of ideas, in order to form your own opinions.</p> <p>There's already a long list of books on my reading list for 2025. Some of these include books that challenge how we think, explore new ways to lead, or dive into technical details that might not directly apply to my daily work but still spark fresh ideas. Reading broadly doesn't just expand your knowledge—it helps you see problems from new angles and come up with creative solutions. My 2025 reading list is already growing, and I’m excited to discover new insights that could shape how I work and grow in the coming year.</p> Aquileo | Moving my Blog from Hugo to Zola2024-12-15T00:00:00+00:002024-12-15T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/hugo-to-zola/<p>After a brief 6 year hiatus, I felt it was time I started posting to my blog again. Six years is a long time in software, so when I ran <code>hugo serve</code> with the latest version of Hugo, all I got was a blank page. No errors, no logs, just a blank page. Hmm.</p> <p>Hugo, in case you don't know, is now a very popular static site generator, written in Go. But back when I started using it, it was a relatively young upstart with a lot of promise. It comes with some nice-looking templates, live-reload features and a very active community. But right now I was getting a blank page, with no hints where to go next. So, naturally, I went to Google.</p> <p>After some Googling and browsing through the Hugo changelogs and issues (there were not supposed to be any breaking changes between the version I had last used and the latest), I still didn't know why I was getting a blank page. Well, I thought, maybe 6 years ago this would have taken ages to solve, but this is 2024, we have AI assistants now, right!? This is a great chance to try out <a href="https://github.com/Aider-AI/aider">Aider</a>! Aider is an AI coding assistant that runs in your terminal and can use all your code as context. I installed it, and asked it to fix the blank page I was facing. Amazingly, within a few iterations, Aider was actually able to update the code in a way that at least resulted in more specific errors. A few more iterations and a bit of Googling later, my blog was working again. Hooray!</p> <p>Except that the styles were all wrong. The theme I was using also wasn't compatible with this version of Hugo anymore, so it needed to be updated. I spent about 30 minutes trying to do so, without success, and it was at this point, 2 hours in, that I was reminded of Hugo's terrible templating system (which, to be fair to Hugo, is based on Go's terrible templating system). Aider wasn't able to come to my aid here. So then a thought occurred: "Hey, writing a static site generator can't be that hard, can it? What if I made my own, with some Jinja-like templating? That would be nice."</p> <p>All memories of actually wanting to write a blog post were long gone by this point, so I cracked open my Go IDE and started coding. After about 30 minutes I had something going with <a href="https://github.com/flosch/pongo2">Pongo</a>, a template engine for Go with Django-like syntax. It had been a few years since I used a non-standard template engine for Go, and it seemed like Pongo didn't really support template inheritance, one of the most important features of Django templates. This is when it became clear that this was going to be a bigger project. And maybe Go isn't the best fit for this... I've been wanting to start a project in Rust for a while now, maybe I should do that?</p> <p>I was just about to run <code>cargo new</code>, but then thought, maybe, just maybe, someone else had the same idea before me. And indeed, when looking for static site generators written in Rust, I found a few candidates. The most promising of those turned out to be <a href="https://www.getzola.org/">Zola</a>. And that, kids, is how I met your <del>mother</del> static site renderer written in Rust.</p> <p>It seemed like the creator of Zola shared my dislike of Hugo's template engine, which instantly made me like it more:</p> <blockquote> <p>This tool and its template engine tera were born from an intense dislike of the (insane) Golang template engine and therefore of Hugo that I was using before for 6+ sites.</p> </blockquote> <p>Zola comes with most of the same features as Hugo: single binary, live-reload, syntax highlighting, Sass compilation, and more, but with a Django-like template syntax (that actually supports template inheritance). I couldn't believe it. Another dream crushed before it even began. I pressed Ctrl+C on the incomplete <code>cargo new</code> command in my terminal, and typed <code>zola init blog</code> instead.</p> <p>Zola's documentation is okay, and I'm not about to bash a free and open source project for not having great documentation, but I did have some trouble initially figuring out the lay of the land, and the documentation didn't really help there. What helped most was finding some example projects, helpfully listed in <a href="https://github.com/getzola/zola/blob/master/EXAMPLES.md">EXAMPLES.md</a>. After looking through a few of those (and their source code), I got the basic idea. You have a <code>content</code> directory, which has subdirectories containing markdown files. In each subdirectory, you can place an <code>_index.md</code> file to control some options and (optionally) render a page at the root of the directory. For example, I put my blog posts in a <code>posts</code> directory like this:</p> <pre data-lang="txt" style="background-color:#eff1f5;color:#4f5b66;" class="language-txt "><code class="language-txt" data-lang="txt"><span>├── posts </span><span>│   ├── 2014-04-14-the-popularity-of-go.md </span><span>│   ├── 2014-04-16-why-is-go-popular-in-china.md </span><span>│   ├── 2014-05-03-efficient-string-concatenation-in-go.md </span><span>│   ├── 2014-05-19-running-the-go-race-detector-with-cover.md </span><span>│   ├── 2014-12-13-solving-regex-crosswords-using-go.md </span><span>│   ├── 2015-01-26-open-source-go-projects-to-learn-from.md </span><span>│   ├── 2015-04-03-building-a-flashcard-app-with-react-native.md </span><span>│   ├── 2017-10-01-bit-manipulation-in-go-1-9.md </span><span>│   ├── 2018-07-09-rules-for-choosing-nonfiction-books.md </span><span>│   └── _index.md </span></code></pre> <p>And inside <code>_index.md</code>, I have this:</p> <pre data-lang="txt" style="background-color:#eff1f5;color:#4f5b66;" class="language-txt "><code class="language-txt" data-lang="txt"><span>+++ </span><span>title = &quot;Herman Schaaf&quot; </span><span>description = &quot;Herman Schaaf&#39;s personal blog and website&quot; </span><span>page_template = &quot;post.html&quot; </span><span>redirect_to = &quot;/&quot; </span><span>[extra] </span><span>+++ </span></code></pre> <p>Something that tripped me up a bit is that there are two similar settings available: <code>template</code> and <code>page_template</code>. It turns out that <code>template</code> is for controlling the template that renders the root page, while <code>page_template</code> refers to the template used by all the other markdown files in the directory.</p> <p>To ensure consistency with my previous Hugo website and not have broken links, I added a <code>path</code> to the frontmatter of all my markdown files, to ensure they are still available at the top-level, like this:</p> <pre data-lang="txt" style="background-color:#eff1f5;color:#4f5b66;" class="language-txt "><code class="language-txt" data-lang="txt"><span>--- </span><span>title: The Popularity of Go </span><span>description: Some observations from Google Trends </span><span>path: /the-popularity-of-go </span><span>slug: the-popularity-of-go </span><span>date: 2014-04-14 </span><span>--- </span></code></pre> <p>Overall, moving from Hugo to Zola was quite easy. I decided to take the opportunity to also write my own styling, rather than use an off-the-shelf theme like I had done previously with Hugo. I had to make some small changes to the markdown frontmatter and content for compatibility with Zola. For example, Zola didn't recognize <code>text</code> or <code>plaintext</code> as a valid language for code snippets, but <code>txt</code> works fine. In the frontmatter, I had to use <code>description</code> rather than <code>subtitle</code>. I also ran into an issue where the hot-reloading feature of Zola sometimes hid errors from me, which took me a while to figure out. After this I learned to stop and restart <code>zola serve</code> if something wasn't acting like I expected, to make sure I'm getting all the errors. But that was basically it.</p> <p>With <a href="https://github.com/getzola/zola">14k+ stars on GitHub</a> at the time of writing, I clearly wasn't the first person to come across Zola. But the work put into it by a passionate community has clearly paid off, and I want to thank everyone who contributed to the project so far. I could have spent the next couple of <del>months</del> years developing this myself, but truth be told, I'm glad I didn't have to. And, hey, look, in the end I actually did write that blog post.</p> Aquileo | 3 Rules for Choosing Nonfiction Books2018-07-09T00:00:00+00:002018-07-09T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/how-i-choose-nonfiction-books/<p>I love reading nonfiction. But, as I am sure you can relate, I only have limited time. Even if we were able to make enough time to read one book a week on average—certainly not the case for me right now—we would still only be able to read around 3,000 books in our entire adult lives. At my current pace, the real number will likely end up being far lower.</p> <p>With this in mind, I want to spend my time reading good books! But how can we identify those, without first reading them? Today I want to share my heuristics for choosing good nonfiction books to read. I will also evaluate them against the books I have read in recent years, to see if they generally match up with my ratings.</p> <p>Before I start, a disclaimer: All of this is, of course, highly subjective. I read nonfiction for enjoyment, and I enjoy nonfiction most when I am learning interesting things, or am guided to think in new ways. Preferably, claims should be backed by peer-reviewed studies, or presented as speculation otherwise. Either way, the author should be clear about this, and unbiased enough to present different sides of the issue.</p> <p>With that out of the way, let's look at the rules.</p> <h2 id="rule-1-prefer-books-by-experts-in-the-field">Rule #1: Prefer books by experts in the field</h2> <p>The best nonfiction books I have read have invariably been by folks who spent their lives researching that particular issue. A couple of books in this category immediately come to mind: <a href="https://amzn.to/2zWkIvO">Why We Sleep</a>, <a href="https://amzn.to/2LBnFDz">The Language Instinct</a>, <a href="https://amzn.to/2A18RwE">Gödel Escher Bach</a>.</p> <p>Positive indicators of this in a blurb may include "Professor in [field directly related to the book's topic]", "Long-time researcher in [field directly related to the book's topic]". Similarly, be wary of terms like "expert", "thought leader" and "journalist".</p> <p>Yes, no books by journalists, unless it's about journalism. If the author is not an expert in the field, they should at least be a long-time practitioner. Malcolm Gladwell's books are enjoyable—there is no denying he tells compelling stories—but ultimately they rely heavily on anecdotal evidence. In general, I find that books by journalists cherry-pick examples to fit the argument, and gloss over (or completely ignore) evidence to the contrary.</p> <p><em>Rule #1 does not apply to Biographies</em>. Biographies are different. You do not need to be a professor of history to write a compelling and informative biography on John D. Rockefeller, nor do you need to have known him personally. I have read many excellent biographies written by journalists. Journalists are experts in the skill of using many disparate sources and weaving it into a compelling narrative. Certainly, this often results in opinionated character depictions, but I believe these are subjective by nature.</p> <h2 id="rule-2-only-accept-goodreads-ratings-of-4-0-and-up">Rule #2: Only accept Goodreads ratings of 4.0 and up</h2> <p>An average rating on Goodreads higher than 4.0 with at least 100 ratings is always good sign. <a href="https://xkcd.com/1098/">This XKCD comic</a> explains this phenomenon well:</p> <a href="https://xkcd.com/1098/"> <img src="https://svbtleusercontent.com/fNvjSjG8nWaQ74GFkCPj6y0xspap.png" title="Star Ratings - XKCD"> </a> <p>Unfortunately, Goodreads ratings are often not unbiased, and many of the reviews are from readers who received free copies in exchange for their reviews. That's why this is only Rule #2. It's also a good idea to read some of the lowest-rated reviews first, to see what the main complaints about the book are, and whether they would put you off reading it.</p> <h2 id="rule-3-no-books-with-the-word-surprising-on-the-cover">Rule #3: No books with the word "surprising" on the cover</h2> <p>This is targeted at books that market themselves as "the surprising truth" or "the surprising science" behind such and such. Often these books are already discarded by Rule #1 and #2. But for when they are not, Rule #3 exists to protect against over-simplification and one-sided arguments. I have not read all books with "surprising" on the cover, but the ones I have, I have found to be lacking.</p> <h1 id="case-studies">Case studies</h1> <p>The following list of books was chosen from my Goodreads history, and I tried to choose a range of books that I rated between 1 star and 5 star. If you are wondering, the ratings were given shortly after I finished the books, and I had not created the 3 rules at that point. Let's see how the rules do against my subjective evaluations of some books I have read:</p> <table class="mainTable"> <tr> <th width="30%">My Rating</th> <th>Notes</th> </tr> <tr> <td style="text-align: center;"><a href="https://www.amazon.com/dp/1501144316/ref=nosim?tag=bbtl00-20" target="_blank" title="Why We Sleep: The New Science of Sleep and Dreams by Matthew Walker"><img alt="Why We Sleep: The New Science of Sleep and Dreams" src="https://images.gr-assets.com/books/1556604137l/34466963.jpg"/></a> <br/>5 stars </td> <td style="vertical-align: top"> <ul style="list-style-type: none; padding-left: 0"> <li style="list-style-type: none;">✓ Written by an expert</li> <li style="list-style-type: none;">✓ Goodreads rating above 4.0</li> <li style="list-style-type: none;">✓ No "surprising"</li> </ul> <a href="https://www.amazon.com/dp/1501144316/ref=nosim?tag=bbtl00-20">Why We Sleep: The New Science of Sleep and Dreams</a> by Matthew Walker is hands down the best nonfiction book I have read this year. It is also the most terrifying. An experienced sleep scientist explains the ins and outs of sleep and the effects of sleep deprivation on human health. I tell just about everyone I meet about how fantastic this book is. And luckily, it also meets all the criteria for a good nonfiction book. Phew! </td> </tr> <tr> <td style="text-align: center;"><a href="https://www.amazon.com/dp/0465026567/ref=nosim?tag=bbtl00-20" target="_blank" title="Gödel, Escher, Bach: An Eternal Golden Braid"><img alt="Gödel, Escher, Bach: An Eternal Golden Braid" src="https://images.gr-assets.com/books/1547125681l/24113.jpg" /></a><br/> 5 stars </td> <td style="vertical-align: top"> <ul style="list-style-type: none; padding-left: 0"> <li style="list-style-type: none;">✓ Written by an expert</li> <li style="list-style-type: none;">✓ Goodreads rating above 4.0</li> <li style="list-style-type: none;">✓ No "surprising"</li> </ul> <a href="https://www.amazon.com/dp/0465026567/ref=nosim?tag=bbtl00-20">Gödel, Escher, Bach: An Eternal Golden Braid</a> ranks among my favorite books of all time. It takes dedication to read it, but the insight (and humor!) in this tome is priceless. Written by a professor of cognitive science and comparative literature at Indiana University, Gödel, Escher, Bach explores common themes in the lives and works of logician Kurt Gödel, artist M. C. Escher, and composer Johann Sebastian Bach, and expounds concepts fundamental to mathematics, symmetry, and intelligence. </td> </tr> <tr> <td style="text-align: center;"><a href="https://www.amazon.com/dp/0307887898/ref=nosim?tag=bbtl00-20" target="_blank" title="The Lean Startup: How Today's Entrepreneurs Use Continuous Innovation to Create Radically Successful Businesses"><img src="https://images.gr-assets.com/books/1333576876l/10127019.jpg"/></a><br/> 5 stars </td> <td style="vertical-align: top"> <ul style="list-style-type: none; padding-left: 0"> <li style="list-style-type: none;">✓ Written by an expert</li> <li style="list-style-type: none;">✓ Goodreads rating above 4.0</li> <li style="list-style-type: none;">✓ No "surprising"</li> </ul> The Lean Startup: How Constant Innovation Creates Radically Successful Businesses, by Eric Ries. As a startup founder, Eric Ries was arguably not an expert in startups when he wrote this book, but he was a long-time practitioner. This book gives excellent advice, and has been a manual used by many successful businesses across the world. </td> </tr> <tr> <td style="text-align: center;"><a href="https://www.amazon.com/dp/0753821826/ref=nosim?tag=bbtl00-20" target="_blank" title="Endless Forms Most Beautiful"><img src="https://images.gr-assets.com/books/1182202467l/1243278.jpg"/></a> <br/> 4 stars </td> <td style="vertical-align: top"> <ul style="list-style-type: none; padding-left: 0"> <li style="list-style-type: none;">✓ Written by an expert</li> <li style="list-style-type: none;">✓ Goodreads rating above 4.0</li> <li style="list-style-type: none;">✓ No "surprising"</li> </ul> <a href="https://amzn.to/2LEVmnV">Endless Forms Most Beautiful</a> is by an expert in evolutionary development, and both its subject and its prose are, indeed, most beautiful. </td> </tr> <tr> <td style="text-align: center;"><a href="https://www.amazon.com/dp/1476733503/ref=nosim?tag=bbtl00-20" target="_blank" title="The Gene: An Intimate History"><img src="https://images.gr-assets.com/books/1463591739l/27276428.jpg"/></a><br/> 4 stars </td> <td style="vertical-align: top"> <ul style="list-style-type: none; padding-left: 0"> <li style="list-style-type: none;">✓Written by an expert</li> <li style="list-style-type: none;">✓Goodreads rating above 4.0</li> <li style="list-style-type: none;">✓No "surprising"</li> </ul> In <a href="https://www.amazon.com/dp/1476733503/ref=nosim?tag=bbtl00-20">The Gene: An Intimate History</a>, Siddhartha Mukherjee relates stories from his childhood and family to bring the realities of the diseases he spent his life studying into focus. It's rare that a book is both as moving and enlightening as this. </td> </tr> <tr> <td style="text-align: center;"><a href="https://www.amazon.com/dp/0807071706/ref=nosim?tag=bbtl00-20" target="_blank" title="Inferior: The true power of women and the science that shows it, by Angela Saini"><img src="https://images.gr-assets.com/books/1483642947l/31869108.jpg"/></a><br/> 3 stars </td> <td style="vertical-align: top"> <ul style="list-style-type: none; padding-left: 0"> <li style="list-style-type: none;">❌ Written by an expert</li> <li style="list-style-type: none;">✓ Goodreads rating above 4.0</li> <li style="list-style-type: none;">✓ No "surprising"</li> </ul> Inferior: The true power of women and the science that shows it, by Angela Saini. This one, being written by a journalist, breaks rule #1. Although it was a compelling read, and I learned some interesting things, the argument was one-sided and ignored a lot of evidence against it. See <a href="https://www.goodreads.com/review/show/2094475969?book_show_action=true&from_review_page=1">this review</a> for a thorough explanation. </td> </tr> <tr> <td style="text-align: center;"><a href="https://www.amazon.com/dp/0062413406/ref=nosim?tag=bbtl00-20" target="_blank" title="Alibaba: The House That Jack Ma Built, by Duncan Clark"><img src="https://images.gr-assets.com/books/1442823028l/25817524.jpg"/></a><br/> 3 stars </td> <td style="vertical-align: top"> <ul style="list-style-type: none; padding-left: 0"> <li style="list-style-type: none;">✓It's mostly a biography</li> <li style="list-style-type: none;">❌ Goodreads rating above 4.0</li> <li style="list-style-type: none;">✓No "surprising"</li> </ul> <a href="https://www.amazon.com/dp/0062413406/ref=nosim?tag=bbtl00-20" target="_blank">Alibaba: The House That Jack Ma Built, by Duncan Clark</a>. An enjoyable read, but not very memorable, nor does it provide much insight into Alibaba itself. </td> </tr> <tr> <td style="text-align: center;"><a href="https://www.amazon.com/dp/0316017922/ref=nosim?tag=bbtl00-20" target="_blank"><img src="https://images.gr-assets.com/books/1344266315l/3228917.jpg"/></a><br/> 3 stars </td> <td style="vertical-align: top"> <ul style="list-style-type: none; padding-left: 0"> <li style="list-style-type: none;">❌ Written by an expert</li> <li style="list-style-type: none;">✓Goodreads rating above 4.0</li> <li style="list-style-type: none;">✓No "surprising"</li> </ul> <a href="https://www.amazon.com/dp/0316017922/ref=nosim?tag=bbtl00-20">Outliers: The Story of Success</a> by Malcolm Gladwell was an entertaining read, there is no denying that. But like other Malcolm Gladwell books, it cherry-picks examples and tries to draw grand conclusions from them. We are left with interesting ideas, but only anecdotal evidence to judge them by. </td> </tr> <tr> <td style="text-align: center;"><a href="https://www.amazon.com/gp/product/0393318486/ref=as_li_ss_tl?ie=UTF8&SubscriptionId=1MGPYB6YW3HWK55XCGG2&linkCode=ll1&tag=bbtl00-20&linkId=ae71fd02c6ba40ee2a96af0a99aeeda3&language=en_US" target="_blank"><img src="https://images.gr-assets.com/books/1387741747l/835623.jpg"/></a><br/> 2 stars </td> <td style="vertical-align: top"> <ul style="list-style-type: none; padding-left: 0"> <li style="list-style-type: none;">✓ Written by an expert</li> <li style="list-style-type: none;">❌ Goodreads rating above 4.0</li> <li style="list-style-type: none;">✓ No "surprising"</li> </ul> <a href="https://www.amazon.com/gp/product/0393318486/ref=as_li_ss_tl?ie=UTF8&SubscriptionId=1MGPYB6YW3HWK55XCGG2&linkCode=ll1&tag=bbtl00-20&linkId=ae71fd02c6ba40ee2a96af0a99aeeda3&language=en_US">How the Mind Works</a>, by Steven Pinker, covers a topic that I am endlessly fascinated by. It is also written by the author of one of my favorite books, The Language Instinct, so I really expected to enjoy this. However, I found it rather dry. You can read my <a href="https://www.goodreads.com/review/show/1791174314?book_show_action=false&from_review_page=1">full review on Goodreads</a>, but I gave it 2 stars at the time. And with a Goodreads rating of 3.97, it only meets 2 of the 3 criteria for choosing good nonfiction books. </td> </tr> <tr> <td style="text-align: center;"><a href="https://www.amazon.com/s?k=Money%3A%2BKnow%2BMore%2C%2BMake%2BMore%2C%2BGive%2BMore%3A%2BLearn%2Bhow%2Bto%2Bmake%2Bmore%2Bmoney%2Band%2Btransform%2Byour%2Blife&tag=bbtl00-20" target="_blank"><img src="https://images.gr-assets.com/books/1497703798l/35451334.jpg"/></a><br/> 1 star </td> <td style="vertical-align: top"> <ul style="list-style-type: none; padding-left: 0"> <li style="list-style-type: none;">❌ Written by an expert</li> <li style="list-style-type: none;">❌ Goodreads rating above 4.0</li> <li style="list-style-type: none;">✓No "surprising"</li> </ul> <a href="https://www.amazon.com/s?k=Money%3A%2BKnow%2BMore%2C%2BMake%2BMore%2C%2BGive%2BMore%3A%2BLearn%2Bhow%2Bto%2Bmake%2Bmore%2Bmoney%2Band%2Btransform%2Byour%2Blife&tag=bbtl00-20">Money: Know More, Make More, Give More</a>, by Rob Moore. I don't even want to talk about how bad this book is. But the fact that it broke two rules is a good indication that the rules are on solid ground. </td> </tr> <tr> <td style="text-align: center;"><a href="https://www.amazon.com/dp/1885167776/ref=nosim?tag=bbtl00-20" target="_blank"><img src="https://images.gr-assets.com/books/1362177469l/16256798.jpg"/></a><br/> 1 star </td> <td style="vertical-align: top"> <ul style="list-style-type: none; padding-left: 0"> <li style="list-style-type: none;">❌ Written by an expert</li> <li style="list-style-type: none;">✓ Goodreads rating above 4.0</li> <li style="list-style-type: none;">❌ No "surprising"</li> </ul> I found <a href="https://www.amazon.com/dp/1885167776/ref=nosim?tag=bbtl00-20">The One Thing: The Surprisingly Simple Truth Behind Extraordinary Results</a> by Gary Keller to be very repetitive, and every example was explained as the person focusing on "one thing", when instead they were clearly focusing on multiple things. Also notice the use of the phrase "surprisingly simple" in the title! <p>In fairness, Goodreads users don't agree with my opinion: they give it an average rating of 4.16. That's fine, and also why I don't rely solely on Goodreads ratings. I'd encourage you to read at least a bit of the book, and see whether or not you agree.</p> </td> </tr> </table> <p>For this small sample of books, the three rules perform remarkably well. I'll admit, I am surprised! On average, I rated books that meet all three criteria 4.6 stars, two out of three criteria 2.75 stars, and one out of three 1 star. Of course, my ratings are completely subjective, but I did give these ratings long before coming up with the rules, and they seem to be a very good guide.</p> <p>Do you use a different set of rules? Or do you have a system that covers fiction books as well? I would love to hear about it.</p> <p>You can also read replies to this on <a href="https://news.ycombinator.com/item?id=19116915">Hacker News</a>.</p> Aquileo | Efficient Bit Manipulation in Go 1.92017-10-01T00:00:00+00:002017-10-01T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/bit-manipulation-in-go-1-9/<p>Go 1.9, released in August 2017, introduced the <a href="https://golang.org/pkg/math/bits"><code>maths/bits</code></a> package. This package provides optimized functions for bit counting and manipulation of unsigned integers. These are useful in low-level environments where the individual bits stored in an integer are of importance. Let's take a look at some of the functions.</p> <p>First up, <code>Len</code> returns the minimum number of bits required to represent a value <code>x</code>.</p> <pre data-lang="go" style="background-color:#eff1f5;color:#4f5b66;" class="language-go "><code class="language-go" data-lang="go"><span style="color:#b48ead;">package </span><span style="color:#bf616a;">main </span><span> </span><span style="color:#b48ead;">import </span><span>( </span><span> &quot;</span><span style="color:#a3be8c;">fmt</span><span>&quot; </span><span> &quot;</span><span style="color:#a3be8c;">math/bits</span><span>&quot; </span><span>) </span><span> </span><span style="color:#b48ead;">func </span><span style="color:#8fa1b3;">main</span><span>() { </span><span> </span><span style="color:#b48ead;">var </span><span style="color:#bf616a;">a </span><span style="color:#b48ead;">uint </span><span>= </span><span style="color:#d08770;">31 </span><span> </span><span style="color:#bf616a;">fmt</span><span>.</span><span style="color:#bf616a;">Printf</span><span>(&quot;</span><span style="color:#a3be8c;">bits.Len(</span><span style="color:#d08770;">%d</span><span style="color:#a3be8c;">) = </span><span style="color:#d08770;">%d</span><span style="color:#96b5b4;">\n</span><span>&quot;, </span><span style="color:#bf616a;">a</span><span>, </span><span style="color:#bf616a;">bits</span><span>.</span><span style="color:#bf616a;">Len</span><span>(</span><span style="color:#bf616a;">a</span><span>)) </span><span> </span><span> </span><span style="color:#bf616a;">a</span><span>++ </span><span> </span><span style="color:#bf616a;">fmt</span><span>.</span><span style="color:#bf616a;">Printf</span><span>(&quot;</span><span style="color:#a3be8c;">bits.Len(</span><span style="color:#d08770;">%d</span><span style="color:#a3be8c;">) = </span><span style="color:#d08770;">%d</span><span style="color:#96b5b4;">\n</span><span>&quot;, </span><span style="color:#bf616a;">a</span><span>, </span><span style="color:#bf616a;">bits</span><span>.</span><span style="color:#bf616a;">Len</span><span>(</span><span style="color:#bf616a;">a</span><span>)) </span><span>} </span></code></pre> <p>Which outputs:</p> <pre data-lang="txt" style="background-color:#eff1f5;color:#4f5b66;" class="language-txt "><code class="language-txt" data-lang="txt"><span>bits.Len(31) = 5 </span><span>bits.Len(32) = 6 </span></code></pre> <p><a href="https://play.golang.org/p/zIu5MjdGWz">Playground link</a></p> <p>This is because 31 is <code>11111</code> in binary, and 32 is <code>100000</code>. The first significant bit is in each case is in position 5 and 6, respectively, when counting positions from the right.</p> <p>This also allows us to easily illustrate the <code>OnesCount</code> function, which returns the number of bits set to 1 in a given number:</p> <pre data-lang="go" style="background-color:#eff1f5;color:#4f5b66;" class="language-go "><code class="language-go" data-lang="go"><span style="color:#b48ead;">package </span><span style="color:#bf616a;">main </span><span> </span><span style="color:#b48ead;">import </span><span>( </span><span> &quot;</span><span style="color:#a3be8c;">fmt</span><span>&quot; </span><span> &quot;</span><span style="color:#a3be8c;">math/bits</span><span>&quot; </span><span>) </span><span> </span><span style="color:#b48ead;">func </span><span style="color:#8fa1b3;">main</span><span>() { </span><span> </span><span style="color:#b48ead;">var </span><span style="color:#bf616a;">a </span><span style="color:#b48ead;">uint </span><span>= </span><span style="color:#d08770;">31 </span><span> </span><span style="color:#bf616a;">fmt</span><span>.</span><span style="color:#bf616a;">Printf</span><span>(&quot;</span><span style="color:#a3be8c;">bits.OnesCount(</span><span style="color:#d08770;">%d</span><span style="color:#a3be8c;">) = </span><span style="color:#d08770;">%d</span><span style="color:#96b5b4;">\n</span><span>&quot;, </span><span style="color:#bf616a;">a</span><span>, </span><span style="color:#bf616a;">bits</span><span>.</span><span style="color:#bf616a;">OnesCount</span><span>(</span><span style="color:#bf616a;">a</span><span>)) </span><span> </span><span> </span><span style="color:#bf616a;">a</span><span>++ </span><span> </span><span style="color:#bf616a;">fmt</span><span>.</span><span style="color:#bf616a;">Printf</span><span>(&quot;</span><span style="color:#a3be8c;">bits.OnesCount(</span><span style="color:#d08770;">%d</span><span style="color:#a3be8c;">) = </span><span style="color:#d08770;">%d</span><span style="color:#96b5b4;">\n</span><span>&quot;, </span><span style="color:#bf616a;">a</span><span>, </span><span style="color:#bf616a;">bits</span><span>.</span><span style="color:#bf616a;">OnesCount</span><span>(</span><span style="color:#bf616a;">a</span><span>)) </span><span>} </span></code></pre> <p>which outputs:</p> <pre data-lang="txt" style="background-color:#eff1f5;color:#4f5b66;" class="language-txt "><code class="language-txt" data-lang="txt"><span>bits.OnesCount(31) = 5 </span><span>bits.OnesCount(32) = 1 </span></code></pre> <p><a href="https://play.golang.org/p/02uqcWMapf">Playground link</a></p> <p>We also have <code>LeadingZeros</code>, <code>TrailingZeros</code>, <code>Reverse</code> and <code>ReverseBytes</code>. Let's use all of these in a single program to demonstrate how they work. In the next example, we use the <code>uint16</code> version of these functions to keep the output short and readable. We could just as easily use the 32-bit, 64-bit or unspecified versions.</p> <pre data-lang="go" style="background-color:#eff1f5;color:#4f5b66;" class="language-go "><code class="language-go" data-lang="go"><span style="color:#b48ead;">package </span><span style="color:#bf616a;">main </span><span> </span><span style="color:#b48ead;">import </span><span>( </span><span> &quot;</span><span style="color:#a3be8c;">fmt</span><span>&quot; </span><span> &quot;</span><span style="color:#a3be8c;">math/bits</span><span>&quot; </span><span> &quot;</span><span style="color:#a3be8c;">os</span><span>&quot; </span><span> &quot;</span><span style="color:#a3be8c;">text/tabwriter</span><span>&quot; </span><span>) </span><span> </span><span style="color:#b48ead;">func </span><span style="color:#8fa1b3;">main</span><span>() { </span><span> </span><span style="color:#a7adba;">// read an unsigned integer from stdin </span><span> </span><span style="color:#b48ead;">var </span><span style="color:#bf616a;">i </span><span style="color:#b48ead;">uint16 </span><span> </span><span style="color:#bf616a;">_</span><span>, </span><span style="color:#bf616a;">err </span><span>:= </span><span style="color:#bf616a;">fmt</span><span>.</span><span style="color:#bf616a;">Scanf</span><span>(&quot;</span><span style="color:#d08770;">%d</span><span>&quot;, &amp;</span><span style="color:#bf616a;">i</span><span>) </span><span> </span><span style="color:#b48ead;">if </span><span style="color:#bf616a;">err </span><span>!= </span><span style="color:#d08770;">nil </span><span>{ </span><span> </span><span style="color:#bf616a;">i </span><span>= </span><span style="color:#d08770;">100 </span><span> </span><span style="color:#bf616a;">fmt</span><span>.</span><span style="color:#bf616a;">Println</span><span>(&quot;</span><span style="color:#a3be8c;">Defaulting to 100</span><span>&quot;) </span><span> } </span><span> </span><span> </span><span style="color:#a7adba;">// create a tabwriter for formatting the output </span><span> </span><span style="color:#bf616a;">w </span><span>:= </span><span style="color:#96b5b4;">new</span><span>(</span><span style="color:#bf616a;">tabwriter</span><span>.</span><span style="color:#b48ead;">Writer</span><span>) </span><span> </span><span> </span><span style="color:#a7adba;">// Format in tab-separated columns with a tab stop of 8. </span><span> </span><span style="color:#bf616a;">w</span><span>.</span><span style="color:#bf616a;">Init</span><span>(</span><span style="color:#bf616a;">os</span><span>.</span><span style="color:#bf616a;">Stdout</span><span>, </span><span style="color:#d08770;">0</span><span>, </span><span style="color:#d08770;">8</span><span>, </span><span style="color:#d08770;">0</span><span>, </span><span style="color:#d08770;">&#39;</span><span style="color:#96b5b4;">\t</span><span style="color:#d08770;">&#39;</span><span>, </span><span style="color:#d08770;">0</span><span>) </span><span> </span><span> </span><span style="color:#bf616a;">fmt</span><span>.</span><span style="color:#bf616a;">Fprintf</span><span>(</span><span style="color:#bf616a;">w</span><span>, &quot;</span><span style="color:#a3be8c;">Binary:</span><span style="color:#96b5b4;">\t</span><span style="color:#d08770;">%0.16b</span><span style="color:#a3be8c;">b</span><span style="color:#96b5b4;">\n</span><span>&quot;, </span><span style="color:#bf616a;">i</span><span>) </span><span> </span><span style="color:#bf616a;">fmt</span><span>.</span><span style="color:#bf616a;">Fprintf</span><span>(</span><span style="color:#bf616a;">w</span><span>, &quot;</span><span style="color:#a3be8c;">Len:</span><span style="color:#96b5b4;">\t</span><span style="color:#d08770;">%16d</span><span style="color:#96b5b4;">\n</span><span>&quot;, </span><span style="color:#bf616a;">bits</span><span>.</span><span style="color:#bf616a;">Len16</span><span>(</span><span style="color:#bf616a;">i</span><span>)) </span><span> </span><span style="color:#bf616a;">fmt</span><span>.</span><span style="color:#bf616a;">Fprintf</span><span>(</span><span style="color:#bf616a;">w</span><span>, &quot;</span><span style="color:#a3be8c;">Ones count:</span><span style="color:#96b5b4;">\t</span><span style="color:#d08770;">%16d</span><span style="color:#96b5b4;">\n</span><span>&quot;, </span><span style="color:#bf616a;">bits</span><span>.</span><span style="color:#bf616a;">OnesCount16</span><span>(</span><span style="color:#bf616a;">i</span><span>)) </span><span> </span><span style="color:#bf616a;">fmt</span><span>.</span><span style="color:#bf616a;">Fprintf</span><span>(</span><span style="color:#bf616a;">w</span><span>, &quot;</span><span style="color:#a3be8c;">Leading zeros:</span><span style="color:#96b5b4;">\t</span><span style="color:#d08770;">%16d</span><span style="color:#96b5b4;">\n</span><span>&quot;, </span><span style="color:#bf616a;">bits</span><span>.</span><span style="color:#bf616a;">LeadingZeros16</span><span>(</span><span style="color:#bf616a;">i</span><span>)) </span><span> </span><span style="color:#bf616a;">fmt</span><span>.</span><span style="color:#bf616a;">Fprintf</span><span>(</span><span style="color:#bf616a;">w</span><span>, &quot;</span><span style="color:#a3be8c;">Trailing zeros:</span><span style="color:#96b5b4;">\t</span><span style="color:#d08770;">%16d</span><span style="color:#96b5b4;">\n</span><span>&quot;, </span><span style="color:#bf616a;">bits</span><span>.</span><span style="color:#bf616a;">TrailingZeros16</span><span>(</span><span style="color:#bf616a;">i</span><span>)) </span><span> </span><span style="color:#bf616a;">fmt</span><span>.</span><span style="color:#bf616a;">Fprintf</span><span>(</span><span style="color:#bf616a;">w</span><span>, &quot;</span><span style="color:#a3be8c;">Reversed:</span><span style="color:#96b5b4;">\t</span><span style="color:#d08770;">%0.16b</span><span style="color:#a3be8c;">b</span><span style="color:#96b5b4;">\n</span><span>&quot;, </span><span style="color:#bf616a;">bits</span><span>.</span><span style="color:#bf616a;">Reverse16</span><span>(</span><span style="color:#bf616a;">i</span><span>)) </span><span> </span><span style="color:#bf616a;">fmt</span><span>.</span><span style="color:#bf616a;">Fprintf</span><span>(</span><span style="color:#bf616a;">w</span><span>, &quot;</span><span style="color:#a3be8c;">Reversed Bytes:</span><span style="color:#96b5b4;">\t</span><span style="color:#d08770;">%0.16b</span><span style="color:#a3be8c;">b</span><span style="color:#96b5b4;">\n</span><span>&quot;, </span><span style="color:#bf616a;">bits</span><span>.</span><span style="color:#bf616a;">ReverseBytes16</span><span>(</span><span style="color:#bf616a;">i</span><span>)) </span><span> </span><span> </span><span style="color:#bf616a;">w</span><span>.</span><span style="color:#bf616a;">Flush</span><span>() </span><span>} </span></code></pre> <p>An example run with 123 as the input gives us the following:</p> <pre data-lang="txt" style="background-color:#eff1f5;color:#4f5b66;" class="language-txt "><code class="language-txt" data-lang="txt"><span>$ go run bits.go </span><span>123 </span><span>Binary: 0000000001111011b </span><span>Len: 7 </span><span>Ones count: 6 </span><span>Leading zeros: 9 </span><span>Trailing zeros: 0 </span><span>Reversed: 1101111000000000b </span><span>Reversed Bytes: 0111101100000000b </span></code></pre> <p><a href="https://play.golang.org/p/_ATMIcdxzV">Playground Link</a></p> <p>It's not difficult to see what these functions do: <code>LeadingZeros</code> returns the number of zeros to the left of the most significant set bit. <code>TrailingZeros</code> returns the number of zeros to the right of the least significant set bit. <code>Reverse</code> returns the number with all bits in reverse order, and <code>ReverseBytes</code> reverses the number byte-by-byte, that is, in 8-bit blocks.</p> <h2 id="how-does-it-perform">How does it perform?</h2> <p>This is all good and well, and it can be useful at times. But some of these functions are relatively simple to implement manually. How much more efficient are the library implementations, really?</p> <p>Let's compare the performance of <code>OnesCount64</code> with that of a naive algorithm for counting the number of set bits. Below is a simple benchmark that compares a naive implementation of <code>OnesCount64</code>, which uses bit shifting to count the number of set bits, to the built-in <code>bits.OnesCount64</code>:</p> <pre data-lang="go" style="background-color:#eff1f5;color:#4f5b66;" class="language-go "><code class="language-go" data-lang="go"><span style="color:#b48ead;">package </span><span style="color:#bf616a;">main </span><span> </span><span style="color:#b48ead;">import </span><span>( </span><span> &quot;</span><span style="color:#a3be8c;">math/bits</span><span>&quot; </span><span> &quot;</span><span style="color:#a3be8c;">testing</span><span>&quot; </span><span>) </span><span> </span><span style="color:#b48ead;">func </span><span style="color:#8fa1b3;">NaiveOnesCount64</span><span>(</span><span style="color:#bf616a;">x </span><span style="color:#b48ead;">uint64</span><span>) </span><span style="color:#b48ead;">int </span><span>{ </span><span> </span><span style="color:#b48ead;">var </span><span style="color:#bf616a;">c</span><span>, </span><span style="color:#bf616a;">i </span><span style="color:#b48ead;">uint64 </span><span> </span><span style="color:#b48ead;">for </span><span style="color:#bf616a;">i </span><span>= </span><span style="color:#d08770;">0</span><span>; </span><span style="color:#bf616a;">i </span><span>&lt; </span><span style="color:#d08770;">64</span><span>; </span><span style="color:#bf616a;">i</span><span>++ { </span><span> </span><span style="color:#bf616a;">c </span><span>+= (</span><span style="color:#bf616a;">x </span><span>&gt;&gt; </span><span style="color:#bf616a;">i</span><span>) &amp; </span><span style="color:#d08770;">1 </span><span> } </span><span> </span><span style="color:#b48ead;">return </span><span style="color:#bf616a;">int</span><span>(</span><span style="color:#bf616a;">c</span><span>) </span><span>} </span><span> </span><span style="color:#b48ead;">var </span><span style="color:#bf616a;">numbers </span><span>= []</span><span style="color:#b48ead;">uint64</span><span>{ </span><span> </span><span style="color:#d08770;">0</span><span>, </span><span style="color:#d08770;">1</span><span>, </span><span style="color:#d08770;">2</span><span>, </span><span style="color:#d08770;">3</span><span>, </span><span style="color:#d08770;">4</span><span>, </span><span style="color:#d08770;">5</span><span>, </span><span style="color:#d08770;">6</span><span>, </span><span style="color:#d08770;">7</span><span>, </span><span style="color:#d08770;">8</span><span>, </span><span style="color:#d08770;">9</span><span>, </span><span> </span><span style="color:#d08770;">100</span><span>, </span><span style="color:#d08770;">200</span><span>, </span><span style="color:#d08770;">300</span><span>, </span><span style="color:#d08770;">400</span><span>, </span><span style="color:#d08770;">500</span><span>, </span><span style="color:#d08770;">600</span><span>, </span><span style="color:#d08770;">700</span><span>, </span><span style="color:#d08770;">800</span><span>, </span><span style="color:#d08770;">900</span><span>, </span><span> </span><span style="color:#d08770;">11111</span><span>, </span><span style="color:#d08770;">22222</span><span>, </span><span style="color:#d08770;">33333</span><span>, </span><span style="color:#d08770;">44444</span><span>, </span><span style="color:#d08770;">55555</span><span>, </span><span style="color:#d08770;">66666</span><span>, </span><span style="color:#d08770;">77777</span><span>, </span><span style="color:#d08770;">88888</span><span>, </span><span style="color:#d08770;">99999</span><span>, </span><span> </span><span style="color:#d08770;">190239012390</span><span>, </span><span style="color:#d08770;">123901312</span><span>, </span><span style="color:#d08770;">6549654</span><span>, </span><span style="color:#d08770;">45904059</span><span>, </span><span>} </span><span> </span><span style="color:#a7adba;">// Output is a global sync variable so that test outputs </span><span style="color:#a7adba;">// do not get elided by the compiler. </span><span style="color:#b48ead;">var </span><span style="color:#bf616a;">Output </span><span style="color:#b48ead;">int </span><span> </span><span style="color:#b48ead;">func </span><span style="color:#8fa1b3;">TestNaiveOnesCount64</span><span>(</span><span style="color:#bf616a;">t </span><span>*</span><span style="color:#bf616a;">testing</span><span>.</span><span style="color:#b48ead;">T</span><span>) { </span><span> </span><span style="color:#bf616a;">m </span><span>:= </span><span style="color:#96b5b4;">len</span><span>(</span><span style="color:#bf616a;">numbers</span><span>) </span><span> </span><span style="color:#b48ead;">for </span><span style="color:#bf616a;">n </span><span>:= </span><span style="color:#d08770;">0</span><span>; </span><span style="color:#bf616a;">n </span><span>&lt; </span><span style="color:#bf616a;">m</span><span>; </span><span style="color:#bf616a;">n</span><span>++ { </span><span> </span><span style="color:#bf616a;">x </span><span>:= </span><span style="color:#bf616a;">numbers</span><span>[</span><span style="color:#bf616a;">n</span><span>%</span><span style="color:#bf616a;">m</span><span>] </span><span> </span><span style="color:#bf616a;">got </span><span>:= </span><span style="color:#bf616a;">NaiveOnesCount64</span><span>(</span><span style="color:#bf616a;">x</span><span>) </span><span> </span><span style="color:#bf616a;">want </span><span>:= </span><span style="color:#bf616a;">bits</span><span>.</span><span style="color:#bf616a;">OnesCount64</span><span>(</span><span style="color:#bf616a;">x</span><span>) </span><span> </span><span style="color:#b48ead;">if </span><span style="color:#bf616a;">got </span><span>!= </span><span style="color:#bf616a;">want </span><span>{ </span><span> </span><span style="color:#bf616a;">t</span><span>.</span><span style="color:#bf616a;">Errorf</span><span>(&quot;</span><span style="color:#a3be8c;">NaiveOnesCount64(</span><span style="color:#d08770;">%d</span><span style="color:#a3be8c;">) = </span><span style="color:#d08770;">%d</span><span style="color:#a3be8c;">, want </span><span style="color:#d08770;">%d</span><span>&quot;, </span><span style="color:#bf616a;">x</span><span>, </span><span style="color:#bf616a;">got</span><span>, </span><span style="color:#bf616a;">want</span><span>) </span><span> } </span><span> } </span><span>} </span><span> </span><span style="color:#b48ead;">func </span><span style="color:#8fa1b3;">BenchmarkNaiveOnesCount64</span><span>(</span><span style="color:#bf616a;">b </span><span>*</span><span style="color:#bf616a;">testing</span><span>.</span><span style="color:#b48ead;">B</span><span>) { </span><span> </span><span style="color:#a7adba;">// run NaiveOnesCount b.N times </span><span> </span><span style="color:#b48ead;">var </span><span style="color:#bf616a;">s </span><span style="color:#b48ead;">int </span><span> </span><span style="color:#b48ead;">for </span><span style="color:#bf616a;">n </span><span>:= </span><span style="color:#d08770;">0</span><span>; </span><span style="color:#bf616a;">n </span><span>&lt; </span><span style="color:#bf616a;">b</span><span>.</span><span style="color:#bf616a;">N</span><span>; </span><span style="color:#bf616a;">n</span><span>++ { </span><span> </span><span style="color:#a7adba;">// use &amp;31 to avoid modulo operation </span><span> </span><span style="color:#bf616a;">s </span><span>+= </span><span style="color:#bf616a;">NaiveOnesCount64</span><span>(</span><span style="color:#bf616a;">numbers</span><span>[</span><span style="color:#bf616a;">n</span><span>&amp;</span><span style="color:#d08770;">31</span><span>]) </span><span> } </span><span> </span><span style="color:#bf616a;">Output </span><span>= </span><span style="color:#bf616a;">s </span><span>} </span><span> </span><span style="color:#b48ead;">func </span><span style="color:#8fa1b3;">BenchmarkBitsOnesCount64</span><span>(</span><span style="color:#bf616a;">b </span><span>*</span><span style="color:#bf616a;">testing</span><span>.</span><span style="color:#b48ead;">B</span><span>) { </span><span> </span><span style="color:#a7adba;">// run NaiveOnesCount b.N times </span><span> </span><span style="color:#b48ead;">var </span><span style="color:#bf616a;">s </span><span style="color:#b48ead;">int </span><span> </span><span style="color:#b48ead;">for </span><span style="color:#bf616a;">n </span><span>:= </span><span style="color:#d08770;">0</span><span>; </span><span style="color:#bf616a;">n </span><span>&lt; </span><span style="color:#bf616a;">b</span><span>.</span><span style="color:#bf616a;">N</span><span>; </span><span style="color:#bf616a;">n</span><span>++ { </span><span> </span><span style="color:#a7adba;">// use &amp;31 to avoid modulo operation </span><span> </span><span style="color:#bf616a;">s </span><span>+= </span><span style="color:#bf616a;">bits</span><span>.</span><span style="color:#bf616a;">OnesCount64</span><span>(</span><span style="color:#bf616a;">numbers</span><span>[</span><span style="color:#bf616a;">n</span><span>&amp;</span><span style="color:#d08770;">31</span><span>]) </span><span> } </span><span> </span><span style="color:#bf616a;">Output </span><span>= </span><span style="color:#bf616a;">s </span><span>} </span></code></pre> <p>Running the comparison on a 64-bit Linux machine, we get:</p> <pre data-lang="txt" style="background-color:#eff1f5;color:#4f5b66;" class="language-txt "><code class="language-txt" data-lang="txt"><span>$ go test -bench=Count </span><span>goos: linux </span><span>goarch: amd64 </span><span>BenchmarkNaiveOnesCount64-4 20000000 71.6 ns/op </span><span>BenchmarkBitsOnesCount64-4 2000000000 1.15 ns/op </span><span>PASS </span><span>ok _/home/code/bits 3.946s </span></code></pre> <p>The built-in <code>OnesCount</code> is orders of magnitude faster! This is because the built-in function uses an algorithm called <em>Parallel summing of adjacent bits</em>. A full description of this is given in Chapter 5 of <a href="http://amzn.to/2x7dQ8n">Hacker's Delight</a>, Counting Bits. (<strong>Correction</strong>: on x86-64 systems, it actually uses POPCNT instructions, see note at bottom of this post).</p> <p>Below is another benchmark comparison, this time for <code>TrailingZeros64</code>:</p> <pre data-lang="go" style="background-color:#eff1f5;color:#4f5b66;" class="language-go "><code class="language-go" data-lang="go"><span style="color:#b48ead;">package </span><span style="color:#bf616a;">main </span><span> </span><span style="color:#b48ead;">import </span><span>( </span><span> &quot;</span><span style="color:#a3be8c;">math/bits</span><span>&quot; </span><span> &quot;</span><span style="color:#a3be8c;">testing</span><span>&quot; </span><span>) </span><span> </span><span style="color:#a7adba;">// NaiveTrailingZeros64 returns the number of trailing zero bits in x; </span><span style="color:#a7adba;">// the result is 64 for x == 0. </span><span style="color:#b48ead;">func </span><span style="color:#8fa1b3;">NaiveTrailingZeros64</span><span>(</span><span style="color:#bf616a;">x </span><span style="color:#b48ead;">uint64</span><span>) </span><span style="color:#b48ead;">int </span><span>{ </span><span> </span><span style="color:#b48ead;">var </span><span style="color:#bf616a;">c</span><span>, </span><span style="color:#bf616a;">i </span><span style="color:#b48ead;">uint64 </span><span> </span><span style="color:#b48ead;">for </span><span style="color:#bf616a;">i </span><span>= </span><span style="color:#d08770;">0</span><span>; </span><span style="color:#bf616a;">i </span><span>&lt; </span><span style="color:#d08770;">64</span><span>; </span><span style="color:#bf616a;">i</span><span>++ { </span><span> </span><span style="color:#b48ead;">if </span><span>(</span><span style="color:#bf616a;">x</span><span>&gt;&gt;</span><span style="color:#bf616a;">i</span><span>)&amp;</span><span style="color:#d08770;">1 </span><span>== </span><span style="color:#d08770;">1 </span><span>{ </span><span> </span><span style="color:#b48ead;">break </span><span> } </span><span> </span><span style="color:#bf616a;">c</span><span>++ </span><span> } </span><span> </span><span style="color:#b48ead;">return </span><span style="color:#bf616a;">int</span><span>(</span><span style="color:#bf616a;">c</span><span>) </span><span>} </span><span> </span><span style="color:#b48ead;">var </span><span style="color:#bf616a;">nums </span><span>= []</span><span style="color:#b48ead;">uint64</span><span>{ </span><span> </span><span style="color:#d08770;">0</span><span>, </span><span style="color:#d08770;">1</span><span>, </span><span style="color:#d08770;">2</span><span>, </span><span style="color:#d08770;">3</span><span>, </span><span style="color:#d08770;">4</span><span>, </span><span style="color:#d08770;">5</span><span>, </span><span style="color:#d08770;">6</span><span>, </span><span style="color:#d08770;">7</span><span>, </span><span style="color:#d08770;">8</span><span>, </span><span style="color:#d08770;">9</span><span>, </span><span> </span><span style="color:#d08770;">100</span><span>, </span><span style="color:#d08770;">200</span><span>, </span><span style="color:#d08770;">300</span><span>, </span><span style="color:#d08770;">400</span><span>, </span><span style="color:#d08770;">500</span><span>, </span><span style="color:#d08770;">600</span><span>, </span><span style="color:#d08770;">700</span><span>, </span><span style="color:#d08770;">800</span><span>, </span><span style="color:#d08770;">900</span><span>, </span><span> </span><span style="color:#d08770;">11111</span><span>, </span><span style="color:#d08770;">22222</span><span>, </span><span style="color:#d08770;">33333</span><span>, </span><span style="color:#d08770;">44444</span><span>, </span><span style="color:#d08770;">55555</span><span>, </span><span style="color:#d08770;">66666</span><span>, </span><span style="color:#d08770;">77777</span><span>, </span><span style="color:#d08770;">88888</span><span>, </span><span style="color:#d08770;">99999</span><span>, </span><span> </span><span style="color:#d08770;">190239012390</span><span>, </span><span style="color:#d08770;">123901312</span><span>, </span><span style="color:#d08770;">6549654</span><span>, </span><span style="color:#d08770;">45904059</span><span>, </span><span>} </span><span> </span><span style="color:#a7adba;">// Output is a global sync variable so that test outputs </span><span style="color:#a7adba;">// do not get elided by the compiler. </span><span style="color:#b48ead;">var </span><span style="color:#bf616a;">Output </span><span style="color:#b48ead;">int </span><span> </span><span style="color:#b48ead;">func </span><span style="color:#8fa1b3;">TestNaiveTrailingZeros64</span><span>(</span><span style="color:#bf616a;">t </span><span>*</span><span style="color:#bf616a;">testing</span><span>.</span><span style="color:#b48ead;">T</span><span>) { </span><span> </span><span style="color:#bf616a;">m </span><span>:= </span><span style="color:#96b5b4;">len</span><span>(</span><span style="color:#bf616a;">nums</span><span>) </span><span> </span><span style="color:#b48ead;">for </span><span style="color:#bf616a;">n </span><span>:= </span><span style="color:#d08770;">0</span><span>; </span><span style="color:#bf616a;">n </span><span>&lt; </span><span style="color:#bf616a;">m</span><span>; </span><span style="color:#bf616a;">n</span><span>++ { </span><span> </span><span style="color:#bf616a;">x </span><span>:= </span><span style="color:#bf616a;">nums</span><span>[</span><span style="color:#bf616a;">n</span><span>%</span><span style="color:#bf616a;">m</span><span>] </span><span> </span><span style="color:#bf616a;">got </span><span>:= </span><span style="color:#bf616a;">NaiveTrailingZeros64</span><span>(</span><span style="color:#bf616a;">x</span><span>) </span><span> </span><span style="color:#bf616a;">want </span><span>:= </span><span style="color:#bf616a;">bits</span><span>.</span><span style="color:#bf616a;">TrailingZeros64</span><span>(</span><span style="color:#bf616a;">x</span><span>) </span><span> </span><span style="color:#b48ead;">if </span><span style="color:#bf616a;">got </span><span>!= </span><span style="color:#bf616a;">want </span><span>{ </span><span> </span><span style="color:#bf616a;">t</span><span>.</span><span style="color:#bf616a;">Errorf</span><span>(&quot;</span><span style="color:#a3be8c;">NaiveTrailingZeros64(</span><span style="color:#d08770;">%d</span><span style="color:#a3be8c;">) = </span><span style="color:#d08770;">%d</span><span style="color:#a3be8c;">, want </span><span style="color:#d08770;">%d</span><span>&quot;, </span><span style="color:#bf616a;">x</span><span>, </span><span style="color:#bf616a;">got</span><span>, </span><span style="color:#bf616a;">want</span><span>) </span><span> } </span><span> } </span><span>} </span><span> </span><span style="color:#b48ead;">func </span><span style="color:#8fa1b3;">BenchmarkNaiveTrailingZeros64</span><span>(</span><span style="color:#bf616a;">b </span><span>*</span><span style="color:#bf616a;">testing</span><span>.</span><span style="color:#b48ead;">B</span><span>) { </span><span> </span><span style="color:#a7adba;">// run NaiveOnesCount b.N times </span><span> </span><span style="color:#bf616a;">s </span><span>:= </span><span style="color:#d08770;">0 </span><span> </span><span style="color:#b48ead;">for </span><span style="color:#bf616a;">n </span><span>:= </span><span style="color:#d08770;">0</span><span>; </span><span style="color:#bf616a;">n </span><span>&lt; </span><span style="color:#bf616a;">b</span><span>.</span><span style="color:#bf616a;">N</span><span>; </span><span style="color:#bf616a;">n</span><span>++ { </span><span> </span><span style="color:#bf616a;">s </span><span>+= </span><span style="color:#bf616a;">NaiveTrailingZeros64</span><span>(</span><span style="color:#bf616a;">nums</span><span>[</span><span style="color:#bf616a;">n</span><span>&amp;</span><span style="color:#d08770;">31</span><span>]) </span><span> } </span><span> </span><span style="color:#bf616a;">Output </span><span>= </span><span style="color:#bf616a;">s </span><span>} </span><span> </span><span style="color:#b48ead;">func </span><span style="color:#8fa1b3;">BenchmarkBitsTrailingZeros64</span><span>(</span><span style="color:#bf616a;">b </span><span>*</span><span style="color:#bf616a;">testing</span><span>.</span><span style="color:#b48ead;">B</span><span>) { </span><span> </span><span style="color:#a7adba;">// run NaiveOnesCount b.N times </span><span> </span><span style="color:#bf616a;">s </span><span>:= </span><span style="color:#d08770;">0 </span><span> </span><span style="color:#b48ead;">for </span><span style="color:#bf616a;">n </span><span>:= </span><span style="color:#d08770;">0</span><span>; </span><span style="color:#bf616a;">n </span><span>&lt; </span><span style="color:#bf616a;">b</span><span>.</span><span style="color:#bf616a;">N</span><span>; </span><span style="color:#bf616a;">n</span><span>++ { </span><span> </span><span style="color:#bf616a;">s </span><span>+= </span><span style="color:#bf616a;">bits</span><span>.</span><span style="color:#bf616a;">TrailingZeros64</span><span>(</span><span style="color:#bf616a;">nums</span><span>[</span><span style="color:#bf616a;">n</span><span>&amp;</span><span style="color:#d08770;">31</span><span>]) </span><span> } </span><span> </span><span style="color:#bf616a;">Output </span><span>= </span><span style="color:#bf616a;">s </span><span>} </span><span> </span></code></pre> <p>Running this we get:</p> <pre data-lang="txt" style="background-color:#eff1f5;color:#4f5b66;" class="language-txt "><code class="language-txt" data-lang="txt"><span>$ go test -bench=Trailing </span><span>goos: linux </span><span>goarch: amd64 </span><span>BenchmarkNaiveTrailingZeros64-4 300000000 5.86 ns/op </span><span>BenchmarkBitsTrailingZeros64-4 2000000000 1.00 ns/op </span><span>PASS </span><span>ok _/home/code/bits 4.460s </span></code></pre> <p>Again, we see that <code>TrailingZeros64</code> is significantly more efficient than the naive solution, though not by as much in this case. This time the reason is that it implements an algorithm first described by Charles E. Leiserson, Harald Prokop and Keith H. Randallin in 1998, using what is called De Bruijn numbers. It's a neat trick, and I recommend reading <a href="http://supertech.csail.mit.edu/papers/debruijn.pdf">the original paper</a>. (<strong>Correction</strong>: Again, it was pointed out that this is not true on most 64-bit systems, see note at the bottom of post)</p> <h2 id="example-use-case">Example Use Case</h2> <p>Now that we have seen that the built-in functions are much faster, you may still be asking "why should I care?" Bit manipulation isn't all that common of a thing to need, and we still have to shift left and right with the <code>&lt;&lt;</code> and <code>&gt;&gt;</code> operators when we do need it, so this is a fair question.</p> <p>One example where this has already made an impact is in Will Fitzgerald's <a href="https://github.com/willf/bitset">bitset package</a>. A <a href="https://lemire.me/blog/2017/08/25/quantifying-the-performance-benefits-of-go-1-9-on-bitsets/">blog post by Daniel Lemire</a> shows how changing to use the new bits package made the <code>Count</code> function run twice as fast, and the previous implementation relied on hand-tuned assembly code!</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>$ go test -run=XXX -bench Count &gt;old.txt </span><span>$ git checkout go19 </span><span>$ go get golang.org/x/tools/cmd/benchcmp </span><span>$ go test -run=XXX -bench Count &gt;new.txt </span><span>$ benchcmp old.txt new.txt </span><span>benchmark old ns/op new ns/op delta </span><span>BenchmarkCount-4 2676 1352 -49.48% </span><span>BenchmarkLemireCount-4 2569228 1401137 -45.46% </span></code></pre> <p>Bitsets are commonly used in all sorts of applications that are memory or space-aware, from databases (e.g. bloom filters) to priority queues and examining stream data. The use of the new <code>bits</code> package in bitsets provides an example of the positive impact this will have on the performance of many Go applications.</p> <h2 id="conclusion">Conclusion</h2> <p>The new bit manipulation package in Go 1.9 wasn't celebrated all that much, but it is pretty exciting if you do low-level programming in Go. It can provide quick efficiency gains if you are already using some of these common functions in a hot path in your code. Kudos to Robert Griesemer, Roman Budnikov and Lynn Boger, who worked on getting this into the standard library.</p> <hr /> <p><strong>Update</strong>: some great feedback from giovannibajo and klauspost on <a href="https://www.reddit.com/r/golang/comments/73lrym/efficient_bit_manipulation_in_go_19/">r/golang</a> - apparently a lot of the efficiency of the OnesCount and TrailingZeros on 64-bit systems comes from calling <a href="https://github.com/golang/go/blob/master/src/cmd/compile/internal/gc/ssa.go#L2758">POPCNT and BSF instructions when the CPU supports it</a>. The algorithms mentioned in the article are a fallback if the system does not support these methods.</p> Aquileo | Diary of Building an iOS App with React Native2015-04-03T00:00:00+00:002015-04-03T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/building-a-flashcard-app-with-react-native/<p>When news broke that React Native was open sourced on Friday, I felt elated. This was the moment I had been waiting for. Ever since the <a href="https://code.facebook.com/videos/786462671439502/react-js-conf-2015-keynote-introducing-react-native-/">React.js conference videos</a> introduced React Native in late January, I couldn't wait to get my hands on it.</p> <p>If you don't know React Native, it's a new open source framework by Facebook that allows you to write iOS (and, eventually, Android) apps using HTML-like code (called JSX) and JavaScript. It's based on the popular React.js JavaScript framework, but you can't run normal React.js code directly as an iOS app. Instead, once you are familiar with the basics of React.js, you can use the same knowledge, but slightly different components, to assemble iOS and Android apps. They refer to it as "Learn once, write anywhere", as opposed to the "Write once, run anywhere" approach advocated by other frameworks, like PhoneGap. The paradigm employed by React Native allows it to use <em>native</em> iOS components, and bring about changes to the UI through a JavaScript thread running in the background. In theory, this means the experience should feel native and appear very smooth, even though it's powered by JavaScript. And I was about to find out whether that is true in practice.</p> <p>I downloaded a copy, followed the instructions for creating an app, and within minutes after learning about the launch, I had my first React Native iOS app running.</p> <p><a href="https://svbtleusercontent.com/u1cmj8wd7jmq8a.png"><img src="https://svbtleusercontent.com/u1cmj8wd7jmq8a_small.png" alt="iOS Simulator Screenshot - My first React Native iOS app" /></a></p> <p>If the rest of the process were to be as easy as the start, I was in for a grand time. And React Native did not disappoint.</p> <h3 id="the-first-screen">The first screen</h3> <p>I set out to build a simple app for learning simple Chinese phrases, a very basic spaced-repitition flashcard app. Using Sketch, I quickly came up with what I wanted the study screen to look like:</p> <p><a href="https://svbtleusercontent.com/lf57rtrv2alm5a.png"><img src="https://svbtleusercontent.com/lf57rtrv2alm5a_small.png" alt="Card Revealed.png" /></a></p> <p>With only that in hand, I started assembling components in React. First, the card:</p> <pre data-lang="javascript" style="background-color:#eff1f5;color:#4f5b66;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#b48ead;">var </span><span style="color:#bf616a;">Card </span><span>= </span><span style="color:#bf616a;">React</span><span>.</span><span style="color:#8fa1b3;">createClass</span><span>({ </span><span> </span><span style="color:#8fa1b3;">render</span><span>: </span><span style="color:#b48ead;">function</span><span>() { </span><span> </span><span style="color:#b48ead;">var </span><span style="color:#bf616a;">card </span><span>= </span><span style="color:#bf616a;">this</span><span>.</span><span style="color:#bf616a;">props</span><span>.</span><span style="color:#bf616a;">card</span><span>; </span><span> </span><span> </span><span style="color:#b48ead;">return </span><span>( </span><span> &lt;</span><span style="color:#bf616a;">View style</span><span>={styles.</span><span style="color:#bf616a;">card</span><span>}&gt; </span><span> &lt;</span><span style="color:#d08770;">Text </span><span style="color:#bf616a;">numberOfLines</span><span>={</span><span style="color:#d08770;">2</span><span>} </span><span style="color:#bf616a;">ref</span><span>=&quot;</span><span style="color:#a3be8c;">definition</span><span>&quot; </span><span style="color:#bf616a;">style</span><span>={styles.</span><span style="color:#bf616a;">definition</span><span>}&gt;{card.</span><span style="color:#bf616a;">definition</span><span>}&lt;/</span><span style="color:#d08770;">Text</span><span>&gt; </span><span> &lt;</span><span style="color:#d08770;">Text </span><span style="color:#bf616a;">numberOfLines</span><span>={</span><span style="color:#d08770;">1</span><span>} </span><span style="color:#bf616a;">style</span><span>={styles.</span><span style="color:#bf616a;">chinese</span><span>}&gt;{this.props.simplified ? card.simplified : </span><span style="color:#bf616a;">card</span><span>.</span><span style="color:#bf616a;">traditional</span><span>}&lt;/</span><span style="color:#d08770;">Text</span><span>&gt; </span><span> &lt;</span><span style="color:#d08770;">Text </span><span style="color:#bf616a;">ref</span><span>=&quot;</span><span style="color:#a3be8c;">pinyin</span><span>&quot; </span><span style="color:#bf616a;">numberOfLines</span><span>={</span><span style="color:#d08770;">1</span><span>} </span><span style="color:#bf616a;">style</span><span>={styles.</span><span style="color:#bf616a;">pinyin</span><span>}&gt;{card.</span><span style="color:#bf616a;">pinyin</span><span>}&lt;/</span><span style="color:#d08770;">Text</span><span>&gt; </span><span> &lt;/</span><span style="color:#bf616a;">View</span><span>&gt; </span><span> ); </span><span> }, </span><span>}); </span></code></pre> <p>I had never worked with flexbox before, but I found <a href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/">these</a> <a href="https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Flexible_boxes">tutorials</a> particularly useful. React Native's flexbox implementation departs from the browser-equivalent in some small ways:</p> <ul> <li>The shorthand form, e.g. <code>flex: 1 100%</code>, is not valid in React Native. Instead, specify the properties separately: <code>flex: 1; width: 100%</code>. Flex values can be integers or doubles (<code>0</code>, <code>0.3</code>, <code>1.0</code> etc), indicating the relative size of the box. So, if you have multiple elements they will fill the relative proportion of the view based on their flex value. If you had two views, one with <code>flex 0.25</code> and the other with <code>flex 0.75</code>, it would split into quarters. Or you could also get an identical effect using <code>flex 25</code> and <code>flex 75</code>.</li> <li>Item-based styling is limited: <code>flex-basis</code>, <code>flex-shrink</code> and <code>flex-grow</code> are not available. I hope this will change in the future, but as of now the workaround is to use container properties to achieve the same effects.</li> </ul> <p>For the card, this is what my styles looked like at first:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span> card: { </span><span> flexDirection: &#39;column&#39;, </span><span> justifyContent: &#39;center&#39;, </span><span> alignItems: &#39;center&#39;, </span><span> backgroundColor: &#39;white&#39;, </span><span> shadowColor: &quot;black&quot;, </span><span> shadowOpacity: 0.3, </span><span> shadowRadius: 3, </span><span> shadowOffset: { </span><span> height: 0, </span><span> width: 0 </span><span> }, </span><span> width: 290, </span><span> height: 290, </span><span> }, </span><span> definition: { </span><span> fontSize: 30, </span><span> textAlign: &#39;center&#39;, </span><span> }, </span><span> chinese: { </span><span> flex: 1, </span><span> fontSize: 80, </span><span> }, </span><span> pinyin: { </span><span> flex: 1, </span><span> fontSize: 40, </span><span> }, </span></code></pre> <p>No big surprises there.</p> <p>There is a bug in the current version of React Native that causes <a href="https://github.com/facebook/react-native/issues/449">shadows to not work</a> on iPhone 5 and up, but hopefully that will be fixed soon. I might even take a stab at it myself.</p> <p>The rest of the items on the screen were similarly created; buttons, progress bar, all were simple React components drawn onto the screen and styled by flexbox. If you are somewhat familiar with React.js, this should prove to be very easy.</p> <h3 id="adding-a-listview">Adding a ListView</h3> <p>With the study screen cleared, I decided to move on to adding a ListView, for choosing which deck of cards you would like to study. I didn't even design this in Sketch, I had a look at the <a href="https://github.com/facebook/react-native/tree/master/Examples/Movies">Movies example</a> to see how they did it there, and used a similar approach.</p> <p>The important part is the <code>render</code> function, which returns something that looks like this:</p> <pre data-lang="javascript" style="background-color:#eff1f5;color:#4f5b66;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#b48ead;">return </span><span>( </span><span> &lt;</span><span style="color:#bf616a;">View style</span><span>={{flex: </span><span style="color:#d08770;">1</span><span>, flexDirection: &#39;</span><span style="color:#a3be8c;">column</span><span>&#39;}}&gt; </span><span> {</span><span style="color:#bf616a;">header</span><span>} </span><span> &lt;</span><span style="color:#bf616a;">ListView </span><span> </span><span style="color:#bf616a;">ref</span><span>=&quot;</span><span style="color:#a3be8c;">listview</span><span>&quot; </span><span> </span><span style="color:#bf616a;">dataSource</span><span>={this.state.</span><span style="color:#bf616a;">dataSource</span><span>} </span><span> </span><span style="color:#bf616a;">renderFooter</span><span>={this.</span><span style="color:#bf616a;">renderFooter</span><span>} </span><span> </span><span style="color:#bf616a;">renderRow</span><span>={this.</span><span style="color:#bf616a;">renderRow</span><span>} </span><span> </span><span style="color:#bf616a;">onEndReached</span><span>={this.</span><span style="color:#bf616a;">onEndReached</span><span>} </span><span> </span><span style="color:#bf616a;">automaticallyAdjustContentInsets</span><span>={</span><span style="color:#bf616a;">false</span><span>} </span><span> </span><span style="color:#bf616a;">keyboardDismissMode</span><span>=&quot;</span><span style="color:#a3be8c;">onDrag</span><span>&quot; </span><span> </span><span style="color:#bf616a;">keyboardShouldPersistTaps</span><span>={</span><span style="color:#bf616a;">true</span><span>} </span><span> </span><span style="color:#bf616a;">showsVerticalScrollIndicator</span><span>={</span><span style="color:#bf616a;">true</span><span>} </span><span> </span><span style="color:#bf616a;">style</span><span>={styles.</span><span style="color:#bf616a;">list</span><span>} </span><span> /&gt; </span><span> &lt;/</span><span style="color:#bf616a;">View</span><span>&gt; </span><span> ); </span></code></pre> <p>Notice the <code>this.renderRow</code> and other functions. These I had to implement, but it was as easy as creating elements on the study screen. Here is what the <code>renderRow</code> method looks like:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span> renderRow: function(deck: Object) { </span><span> return ( </span><span> &lt;DeckCell </span><span> onSelect={() =&gt; this.selectDeck(deck)} </span><span> deck={deck} </span><span> /&gt; </span><span> ); </span><span> }, </span></code></pre> <p><code>DeckCell</code> is a simple component I defined to display the deck's title and the progress percentage, like this:</p> <p><a href="https://svbtleusercontent.com/yiqlqceakk3gw.png"><img src="https://svbtleusercontent.com/yiqlqceakk3gw_small.png" alt="iOS Simulator Screen Shot Apr 1, 2015, 14.04.17.png" /></a></p> <p>Upon selecting the <code>DeckCell</code> row, we call <code>this.selectDeck(deck)</code>, which looks like this:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span> selectDeck: function(deck){ </span><span> // onPress is what we do when the deck is selected. We bind it to the current scope. </span><span> var onPress = function(){ </span><span> this.setState({loaded: false}); </span><span> DatabaseManager.loadDecks(this.loadDecks); // we will talk about this in a second </span><span> DatabaseManager.loadProgress(this.loadProgress); </span><span> }.bind(this); </span><span> this.props.navigator.push({ </span><span> title: deck.title, </span><span> component: StudyScreen, // the screen with the cards we created earlier </span><span> passProps: {deck: deck, onPress: onPress}, </span><span> }); </span><span> } </span></code></pre> <p>With this in place, clicking on a deck in the list will load the study screen, sending it a <code>deck</code> object so that we know which deck is intended to be used. So far so good. The last step that remained was to hook up a database. This was the part I was really curious about!</p> <h3 id="hooking-up-a-database">Hooking up a database</h3> <p>After clearing the first hurdle of drawing some items onto the screen and setting up a ListView with dummy objects, my attention turned to hooking up a database and having it communicate with React Native. I noticed that there is an <code>AsyncStorage</code> class, but this is intended for simple key-value storage (like <code>LocalStorage</code> on the web). I needed to do some heavy lifting, so I turned to my trusty friend, SQLite. The question was just, how do I hook SQLite up to the React app? I already alluded to the answer in the previous code snippet.</p> <p>I made use of the <code>RCTBridgeModule</code> interface described in the <a href="http://facebook.github.io/react-native/docs/nativemodulesios.html#content">React Native docs</a>. First, this meant defining a <code>DatabaseManager.h</code> header file:</p> <pre data-lang="Objective-c" style="background-color:#eff1f5;color:#4f5b66;" class="language-Objective-c "><code class="language-Objective-c" data-lang="Objective-c"><span style="color:#b48ead;">#ifndef</span><span> ReactCards_DatabaseManager_h </span><span style="color:#b48ead;">#define </span><span>ReactCards_DatabaseManager_h </span><span> </span><span style="color:#b48ead;">#import </span><span>&quot;</span><span style="color:#a3be8c;">RCTBridgeModule.h</span><span>&quot; </span><span style="color:#b48ead;">#import </span><span>&quot;</span><span style="color:#a3be8c;">DBManager.h</span><span>&quot; </span><span> </span><span style="color:#b48ead;">@interface </span><span>DatabaseManager </span><span style="color:#8fa1b3;">: </span><span style="color:#a3be8c;">NSObject </span><span>&lt;RCTBridgeModule&gt; </span><span style="color:#b48ead;">@property </span><span>(</span><span style="color:#b48ead;">nonatomic</span><span>, </span><span style="color:#b48ead;">strong</span><span>) DBManager *dbManager; </span><span style="color:#b48ead;">@end </span><span> </span><span style="color:#b48ead;">#endif </span></code></pre> <p>Next, I implemented an initialization function to load up the SQLite database and save the connection as an instance variable. The SQLite-specific code was kept in another library, <code>SQLiteManager</code>, which could easily be replaced with a wrapper for Core Data or Realm.</p> <pre data-lang="Objective-c" style="background-color:#eff1f5;color:#4f5b66;" class="language-Objective-c "><code class="language-Objective-c" data-lang="Objective-c"><span> </span><span style="color:#b48ead;">#import </span><span>&lt;</span><span style="color:#a3be8c;">Foundation/Foundation.h</span><span>&gt; </span><span style="color:#b48ead;">#import </span><span>&quot;</span><span style="color:#a3be8c;">SQLiteManager.h</span><span>&quot; </span><span style="color:#b48ead;">#import </span><span>&quot;</span><span style="color:#a3be8c;">RCTLog.h</span><span>&quot; </span><span style="color:#b48ead;">#import </span><span>&quot;</span><span style="color:#a3be8c;">SuperMemo.h</span><span>&quot; </span><span> </span><span style="color:#b48ead;">@implementation </span><span>DatabaseManager </span><span> </span><span>- (</span><span style="color:#b48ead;">id</span><span>)</span><span style="color:#8fa1b3;">init </span><span>{ </span><span> </span><span style="color:#b48ead;">return </span><span>[</span><span style="color:#bf616a;">self </span><span style="color:#8fa1b3;">initWithDB:</span><span>@&quot;</span><span style="color:#a3be8c;">collections.db</span><span>&quot;]; </span><span>} </span><span> </span><span>- (</span><span style="color:#b48ead;">id</span><span>)</span><span style="color:#8fa1b3;">initWithDB:</span><span>(</span><span style="color:#d08770;">NSString</span><span>*)databaseName </span><span>{ </span><span> </span><span style="color:#bf616a;">self </span><span>= [</span><span style="color:#bf616a;">super </span><span style="color:#8fa1b3;">init</span><span>]; </span><span> </span><span style="color:#b48ead;">if </span><span>(</span><span style="color:#bf616a;">self</span><span>) { </span><span> </span><span style="color:#bf616a;">self</span><span>.</span><span style="color:#bf616a;">dbManager </span><span>= [[SQLiteManager </span><span style="color:#8fa1b3;">alloc</span><span>] initWithDatabaseFilename:databaseName]; </span><span> } </span><span> </span><span style="color:#b48ead;">return </span><span style="color:#bf616a;">self</span><span>; </span><span>} </span></code></pre> <p>Now, I could define a <code>loadDecks</code> method that takes a callback function, and export it to the JavaScript code by putting a line at the top of the file</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>RCT_EXPORT_MODULE(); </span></code></pre> <p>and adding a call to <s><code>RCT_EXPORT();</code> on the first line</s> the RCT_EXPORT_METHOD macro (<code>RCT_EXPORT</code> is now deprecated in favor of <code>RCT_EXPORT_METHOD</code>):</p> <pre data-lang="Objective-c" style="background-color:#eff1f5;color:#4f5b66;" class="language-Objective-c "><code class="language-Objective-c" data-lang="Objective-c"><span> </span><span style="color:#bf616a;">RCT_EXPORT_METHOD</span><span>(loadDecks:(RCTResponseSenderBlock)callback </span><span>{ </span><span> </span><span style="color:#a7adba;">// returns an array of NSDictionary objects, with fields id, title, count and percentage </span><span> </span><span style="color:#d08770;">NSArray </span><span>*decks = [</span><span style="color:#bf616a;">self</span><span>.</span><span style="color:#bf616a;">dbManager</span><span> loadDecks]; </span><span> </span><span> </span><span style="color:#bf616a;">callback</span><span>(@[[</span><span style="color:#d08770;">NSNull </span><span style="color:#8fa1b3;">null</span><span>], decks]); </span><span>} </span></code></pre> <p>I didn't expect this to work at all, but I'd gone this far, so there was no turning back. In the JavaScript code, I added lines to first import the DataManager class, and then call the <code>loadDecks</code> function, giving it a callback function the logs the result.</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>var DatabaseManager = require(&#39;NativeModules&#39;).DatabaseManager; </span><span>... </span><span>DatabaseManager.loadDecks(this.loadDecks); </span><span>... </span><span>loadDecks: function(error, decks){ </span><span> if (error) { </span><span> console.error(error); </span><span> } else { </span><span> console.log(decks); </span><span> } </span><span>} </span></code></pre> <p>To my own astonishment, it worked! On first try! In the logs, I could see:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>RCTJSLog&gt; [{&quot;title&quot;:&quot;Descriptions&quot;,&quot;id&quot;:&quot;1&quot;,&quot;count&quot;:&quot;0&quot;,&quot;percentage&quot;:&quot;0&quot;},{&quot;title&quot;:&quot;Phrases&quot;,&quot;id&quot;:&quot;2&quot;,&quot;count&quot;:&quot;0&quot;,&quot;percentage&quot;:&quot;0&quot;},{&quot;title&quot;:&quot;Time&quot;,&quot;id&quot;:&quot;3&quot;,&quot;count&quot;:&quot;0&quot;,&quot;percentage&quot;:&quot;0&quot;},{&quot;title&quot;:&quot;Actions&quot;,&quot;id&quot;:&quot;4&quot;,&quot;count&quot;:&quot;0&quot;,&quot;percentage&quot;:&quot;0&quot;}] </span></code></pre> <p>I now had all the basic components necessary to make a React Native app backed by a SQLite database. It was plain sailing up to this point, and it was plain sailing from there on out. React Native is awesome.</p> <h3 id="submitting-the-app">Submitting the app</h3> <p>I spent some free hours here and there over the next couple of days to finish the app and bring all the parts together. There were no big surprises, and that's a good thing.</p> <p>But before I could submit my app to the app store, I needed to turn off developer mode and enable performance optimizations. It took me some digging to find out how to do this, but this turned out to be quite easy, once you know how. First, inside the project <code>AppDelegate.m</code>, uncomment the line below the <code>OPTION 2</code> comment:</p> <pre data-lang="objective-c" style="background-color:#eff1f5;color:#4f5b66;" class="language-objective-c "><code class="language-objective-c" data-lang="objective-c"><span>jsCodeLocation = [[</span><span style="color:#d08770;">NSBundle </span><span style="color:#8fa1b3;">mainBundle</span><span>] URLForResource:@&quot;</span><span style="color:#a3be8c;">main</span><span>&quot; withExtension:@&quot;</span><span style="color:#a3be8c;">jsbundle</span><span>&quot;]; </span></code></pre> <p>This causes the app to load from pre-bundled file on disk. Next, with the development server running (started with <code>npm start</code>), you can <code>curl</code> the URL in the comment, adding an extra <code>dev</code> query parameter:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>curl http://localhost:8081/index.ios.bundle\?dev\=0 -o main.jsbundle </span></code></pre> <p>The <code>dev</code> query parameter may be set to either <code>0</code> or <code>false</code> (or similarly, <code>1</code> and <code>true</code>). When you next build the app, the log should indicate that development mode is now turned off and performance optimizations are turned on:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>RCTJSLog&gt; &quot;Running application &quot;HSK1 Chinese&quot; with appParams: {&quot;rootTag&quot;:1,&quot;initialProps&quot;:{}}. __DEV__ === false, development-level warning are OFF, performance optimizations are ON&quot; </span></code></pre> <p>With this out of the way, I could build an archive and submit it to the app store. Again, easy as pie.</p> <p>I had some friends beta test the app on different platforms through TestFlight, and all seemed well. And today, one week after the launch of React Native, I submitted the app to be reviewed for the App Store. Wish me luck!</p> <h3 id="closing-remarks">Closing remarks</h3> <p>As a web application developer that was already somewhat familiar with both React.js and iOS, React Native was a total breeze to work with. The things I did not know before I started were easy to learn, thanks to these attributes of React Native:</p> <ul> <li><strong>Error messages are clear and descriptive</strong>. I can't stress the importance of this enough. The React Native error messages would point to specific lines in the JavaScript code, describe the problem exactly, and sometimes even suggest ways to fix it. Once, I did something stupid (I forget what exactly), and it even warned me that what I was doing is an anti-pattern, and should be avoided. Amazing.</li> <li><strong>Instant reload with Cmd+R</strong>. Yup, no need to compile again and again, wasting precious minutes and breaking your concentration. With React Native you can press <code>Cmd+R</code>, and the simulator immediately reloads. This is a <em>huge</em> boon for development.</li> <li><strong>No more storyboards</strong>. Storyboards are great, but they have their downsides. For one, they don't play well with version control systems like Git. For another, I never really liked dragging lines from UI components to lines in the code to indicate a relationship, only to later accidentally remove that line and be faced with an incomprehensible error message. By allowing UIs to be designed with something similar to HTML+CSS, building these just got a whole lot easier and less painful.</li> <li><strong>It's smoooothhh</strong>. Even on my iPhone 4S, the app runs as smooth as the flight of a dream. By running JavaScript in a background thread that doesn't cause the UI to hang, React Native delivers on its promise to make coding iOS apps as easy as writing JavaScript, without the performance problems associated with running it in a WebView. It totally feels just like a native app.</li> </ul> <p>React Native is truly one of the most exciting developments in technology I have seen in a long time, and the wait was worth it. +1, will use again.</p> <p><a href="https://svbtleusercontent.com/rpdhl3zfb92hha.gif"><img src="https://svbtleusercontent.com/rpdhl3zfb92hha_small.gif" alt="fcc5954a6f36e702b590c17a37190922.gif" /></a></p> <hr /> <p><s><strong>PS</strong> If you'd like to check out the app, it's <a href="https://itunes.apple.com/us/app/hsk-level-1-chinese-flashcards/id936639994?ls=1&amp;mt=8">on the app store right now for $0.99</a>. Or if HSK1 is too easy for you, you can sign up to <a href="http://flashcards.herman.asia">be notified when other levels become available</a>.</s> <strong>Update</strong>: Since this was published years ago, iOS introduced some breaking changes that wasn't worthwhile for me to address, and I decided to take the app down. It was fun working on this, thanks for all your support and feedback over the years!</p> <p><strong>Thanks</strong> to Spencer Ahrens from the React Native core team for corrections to this post.</p> Aquileo | Excellent Open Source Go Projects2015-01-26T00:00:00+00:002015-01-26T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/open-source-go-projects-to-learn-from/<p>Something I often hear new Go developers say is that they are looking for some good projects to study, learn from and contribute to. Normally I suggest reading the Go source: it's easy to read, you can pick a part you are interested in, and is probably bound to be the best example of idiomatic Go. But this past weekend presented itself with an opportunity to find more open source Go projects that are of the highest quality.</p> <p>I participated in <a href="http://gophergala.com">Gopher Gala</a>, a 48 hour-long worldwide hackathon for Go. Our team built a project called <a href="http://github.com/gophergala/go_report">Go Report Card</a>, and it aims to rate the quality of any open source Go project on Github. The tool uses various other Go tools: <a href="https://golang.org/cmd/gofmt/">gofmt -s</a> to judge basic formatting, <a href="https://golang.org/cmd/go/#hdr-Run_go_tool_vet_on_packages">go vet</a> to check for suspicious constructs, <a href="https://github.com/fzipp/gocyclo">gocyclo</a> to measure the cyclomatic complexity of the code, and so on. It then averages these results to get the final grade.</p> <p>My teammate <a href="https://twitter.com/shawnps">Shawn Smith</a> and I have been hitting all of the most popular Go repos on Github, and saving the results to a Mongo database. Right now, we have checked <strong>276</strong> of the most popular Go repositories on Github. In order to find the projects of highest quality, I selected the ones with at least 100 Go files, and ordered them by their rating. Here are the results:</p> <table><thead><tr><th>Rank</th><th>Name</th><th>Github Repo</th><th>Go Files</th><th>Score</th></tr></thead><tbody> <tr><td>1</td><td>go-github</td><td><a href="https://github.com/google/go-github">google/go-github</a></td><td>107</td><td>97.1%</td></tr> <tr><td>2</td><td>Cockroach</td><td><a href="https://github.com/cockroachdb/cockroach">cockroachdb/cockroach</a></td><td>204</td><td>89.5%</td></tr> <tr><td>3</td><td>golang/net</td><td><a href="https://github.com/golang/net">golang/net</a></td><td>243</td><td>87.6%</td></tr> <tr><td>4</td><td>xGo</td><td><a href="https://github.com/exercism/xgo">exercism/xgo</a></td><td>142</td><td>86.2%</td></tr> <tr><td>5</td><td>Kubernetes</td><td><a href="https://github.com/GoogleCloudPlatform/kubernetes">GoogleCloudPlatform/kubernetes</a></td><td>636</td><td>83.6%</td></tr> <tr><td>6</td><td>syncthing</td><td><a href="https://github.com/syncthing/syncthing">syncthing/syncthing</a></td><td>146</td><td>83.0%</td></tr> <tr><td>7</td><td>Packer</td><td><a href="https://github.com/mitchellh/packer">mitchellh/packer</a></td><td>656</td><td>82.8%</td></tr> <tr><td>8</td><td>Drone</td><td><a href="https://github.com/drone/drone">drone/drone</a></td><td>189</td><td>82.5%</td></tr> <tr><td>9</td><td>etcd</td><td><a href="https://github.com/coreos/etcd">coreos/etcd</a></td><td>211</td><td>82.3%</td></tr> <tr><td>10</td><td>Docker</td><td><a href="https://github.com/docker/docker">docker/docker</a></td><td>881</td><td>81.4%</td></tr> <tr><td>11</td><td>GoLearn</td><td><a href="https://github.com/sjwhitworth/golearn">sjwhitworth/golearn</a></td><td>111</td><td>80.8%</td></tr> <tr><td>12</td><td>NSQ</td><td><a href="https://github.com/bitly/nsq">bitly/nsq</a></td><td>116</td><td>80.8%</td></tr> <tr><td>13</td><td>GoConvey</td><td><a href="https://github.com/smartystreets/goconvey">smartystreets/goconvey</a></td><td>160</td><td>79.5%</td></tr> <tr><td>14</td><td>golang/tools</td><td><a href="https://github.com/golang/tools">golang/tools</a></td><td>452</td><td>79.2%</td></tr> <tr><td>15</td><td>Fleet</td><td><a href="https://github.com/coreos/fleet">coreos/fleet</a></td><td>151</td><td>79.1%</td></tr> <tr><td>16</td><td>LimeText</td><td><a href="https://github.com/limetext/lime">limetext/lime</a></td><td>150</td><td>79.0%</td></tr> <tr><td>17</td><td>Flynn</td><td><a href="https://github.com/flynn/flynn">flynn/flynn</a></td><td>229</td><td>78.4%</td></tr> <tr><td>18</td><td>godropbox</td><td><a href="https://github.com/dropbox/godropbox">dropbox/godropbox</a></td><td>157</td><td>78.1%</td></tr> <tr><td>19</td><td>consul</td><td><a href="https://github.com/hashicorp/consul">hashicorp/consul</a></td><td>182</td><td>77.4%</td></tr> <tr><td>20</td><td>terraform</td><td><a href="https://github.com/hashicorp/terraform">hashicorp/terraform</a></td><td>454</td><td>74.2%</td></tr> </tbody></table> <p>A lot of these are indeed fantastic projects. And if you are looking for a smaller project to start with, here are the highest-rated open source repos with at most 100 Go files:</p> <table><thead><tr><th>Rank</th><th>Name</th><th>Github Repo</th><th>Go Files</th><th>Score</th></tr></thead><tbody> <tr><td>1</td><td>httplogger</td><td><a href="https://github.com/ernesto-jimenez/httplogger">ernesto-jimenez/httplogger</a></td><td>2</td><td>100.0%</td></tr> <tr><td>2</td><td>Google Analytics Beacon Analytics</td><td><a href="https://github.com/igrigorik/ga-beacon">igrigorik/ga-beacon</a></td><td>1</td><td>99.6%</td></tr> <tr><td>3</td><td>s3cache</td><td><a href="https://github.com/sourcegraph/s3cache">sourcegraph/s3cache</a></td><td>1</td><td>99.4%</td></tr> <tr><td>4</td><td>watchreadlisten</td><td><a href="https://github.com/shawnps/watchreadlisten">shawnps/watchreadlisten</a></td><td>1</td><td>99.3%</td></tr> <tr><td>5</td><td>go-loggly</td><td><a href="https://github.com/segmentio/go-loggly">segmentio/go-loggly</a></td><td>1</td><td>99.3%</td></tr> <tr><td>6</td><td>Exercism CLI</td><td><a href="https://github.com/exercism/cli">exercism/cli</a></td><td>27</td><td>99.0%</td></tr> <tr><td>7</td><td>TextQL</td><td><a href="https://github.com/dinedal/textql">dinedal/textql</a></td><td>1</td><td>98.7%</td></tr> <tr><td>8</td><td>exercism/arkov</td><td><a href="https://github.com/exercism/arkov">exercism/arkov</a></td><td>9</td><td>97.2%</td></tr> <tr><td>9</td><td>go-prompt</td><td><a href="https://github.com/segmentio/go-prompt">segmentio/go-prompt</a></td><td>6</td><td>95.8%</td></tr> <tr><td>10</td><td>SSCC</td><td><a href="https://github.com/pblaszczyk/sscc">pblaszczyk/sscc</a></td><td>14</td><td>94.64%</td></tr> <tr><td>11</td><td>mewmew/playground</td><td><a href="https://github.com/mewmew/playground">mewmew/playground</a></td><td>96</td><td>91.9%</td></tr> <tr><td>12</td><td>go-stats</td><td><a href="https://github.com/segmentio/go-stats">segmentio/go-stats</a></td><td>3</td><td>91.6%</td></tr> <tr><td>13</td><td>localtunnel</td><td><a href="https://github.com/progrium/localtunnel">progrium/localtunnel</a></td><td>3</td><td>91.6%</td></tr> <tr><td>14</td><td>freegeoip</td><td><a href="https://github.com/fiorix/freegeoip">fiorix/freegeoip</a></td><td>9</td><td>91.6%</td></tr> <tr><td>15</td><td>negroni</td><td><a href="https://github.com/codegangsta/negroni">codegangsta/negroni</a></td><td>11</td><td>90.9%</td></tr> <tr><td>16</td><td>MAST</td><td><a href="https://github.com/ikawaha/mast">ikawaha/mast</a></td><td>27</td><td>90.7%</td></tr> <tr><td>17</td><td>martini</td><td><a href="https://github.com/go-martini/martini">go-martini/martini</a></td><td>16</td><td>90.6%</td></tr> <tr><td>18</td><td>Bolt</td><td><a href="https://github.com/boltdb/bolt">boltdb/bolt</a></td><td>42</td><td>90.4%</td></tr> <tr><td>19</td><td>LLVM</td><td><a href="https://github.com/mewlang/llvm">mewlang/llvm</a></td><td>21</td><td>90.4%</td></tr> <tr><td>20</td><td>notify</td><td><a href="https://github.com/rjeczalik/notify">rjeczalik/notify</a></td><td>41</td><td>90.2%</td></tr> </tbody></table> <p>If Go Report Card is to believed, any of these projects a great place to start learning high-quality, idiomatic Go!</p> <hr /> <p>To see how your own open source repo measures up, you can try it out on <a href="http://goreportcard.com">goreportcard</a>. For example, this is what the output looks like for our hackathon project:</p> <p><a href="https://svbtleusercontent.com/bvuvlnxvpcyda.png"><img src="https://svbtleusercontent.com/bvuvlnxvpcyda_small.png" alt="Screen Shot 2015-01-26 at 21.45.17.png" /></a></p> <p>Goreportcard is free and open source, you are welcome to check out the <a href="https://github.com/gojp/goreportcard">Github repo</a> and run checks for your own repo.</p> Aquileo | Solving Regex Crosswords using Go2014-12-13T00:00:00+00:002014-12-13T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/solving-regex-crosswords-using-go/<p><a href="https://svbtleusercontent.com/mm0bqljwaihrg.gif"><img src="https://svbtleusercontent.com/mm0bqljwaihrg_small.gif" alt="ezgif.com-optimize.gif" /></a></p> <p>I first discovered regular expression crosswords two weeks ago, when <a href="http://regexcrossword.com/">RegexCrossword.com</a> appeared on the <a href="https://news.ycombinator.com/item?id=8674039">Hacker News front page</a>. I thoroughly enjoyed the nerdy 30 minutes I spent doing the puzzles, but soon enough my natural inclination towards making computers do my hard work got the better of me: I wanted to solve this with code. This morning I had some free time and decided to give it a shot. It was perhaps even more fun, and definitely more exciting, than solving the puzzles by hand!</p> <p>The end result is about 150 lines of Go code that solves most puzzles in only a few milliseconds. By showing you how I solved it, I hope you'll learn some interesting things about solving puzzles programatically, how regular expressions work and specifically, how they work in Go. Let's dive in.</p> <h3 id="the-algorithm">The algorithm</h3> <p>My first instinct was to solve this with brute force, by iterating over every possible answer to the puzzle, until a solution is found. This would work fine for small puzzles, like when the grid is 2x2. The solutions always use upper case characters, but they may also contain numbers and punctuation. So if we consider those 62 ASCII characters at every cell, the number of possible answers would be 62^4 for the 2x2 grid. At 14,776,336, that's still a manageable search space for a brute force solution. But let's make the grid 4x4, what then? Then we have 62^16, which is around 4.76x10<sup>28</sup>. Not so manageable anymore!</p> <p>Then I thought about the strategy I use when solving these puzzles as a human. For every cell, I check what characters might satisfy the regular expressions for that cell. I then guess one of the options, and continue with the assumption that it's correct. I do this until I find a contradiction, in which case I would go revisit my assumptions. It seems like this strategy of making assumptions and then backtracking when contradictions are found would work well as an algorithm, but there's a snag: regular expression libraries don't normally offer a way to tell whether the expression is matched only at a specific position. You either have to match the entire string, or nothing.</p> <p>It would therefore be great if we had a function that could validate, for any given cell, whether a character could possibly satisfy the regular expression at that position. Let's call this function <code>satisfiesAtPos</code>. It takes a regular expression <code>expr</code>, a character <code>r</code> and a position <code>pos</code>, and tells us whether the character is a possible match for this regular expression at position <code>pos</code>. For example, if we had this simple puzzle:</p> <p><a href="https://svbtleusercontent.com/qexv9owrfhgwuq.png"><img src="https://svbtleusercontent.com/qexv9owrfhgwuq_small.png" alt="beginner beatles.png" /></a></p> <p>then the first cell (in the left-hand upper corner) would be satisfied by H, L, or O based on the horizontal regex <code>HE|LL|O+</code>, so <code>satisfiesAtPos</code> should return <code>true</code> for these characters. Anything that's not in the set {S,P,E,A,K} would satisfy vertical regex <code>[^SPEAK]</code> at position 0, so <code>satisfiesAtPos</code> would return <code>false</code> for {S,P,E,A,K}, and true for all other characters. This means only H will have <code>satisfiesAtPos</code> return <code>true</code> for both regexes at position 0. We can continue in this fashion for every cell, until we reach the end of a row or column, where we should then validate our assumptions for the independent cells. If the row or column doesn't match the regular expression, we backtrack and try other options for the cells where there is more than one possibility.</p> <h3 id="regular-expressions-and-go">Regular Expressions and Go</h3> <p>We have an algorithm! Now we just need to figure out how to write the function <code>satisfiesAtPos</code>. In Go, it's frighteningly easy if you understand some regular expression theory and have the patience to scratch around in the source code. For understanding regular expression theory, <a href="http://swtch.com/~rsc/regexp/regexp1.html">this article by Russ Cox</a> is an excellent introduction on the subject. And since the author is also one of the core contributors to Go, the algorithm described in this article is also the one implemented in the Go <code>regexp</code> package. Convenient!</p> <p>I'm not going to re-iterate everything said in that article, and I highly recommend reading it. But as long as you know that regular expressions can be expressed as state machines, you should be able to follow the rest of this discussion.</p> <p>Normally when you use regular expressions in Go, you call the <code>regexp</code> package. Specifically, you might call <code>regexp.Compile</code>, like this:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>re, err := regexp.Compile(`HE|LL|O+`) </span></code></pre> <p>which gives you <code>re</code>, a <code>*Regexp</code> you can use to call <code>Match</code>, <code>Find</code>, or <code>Replace</code>. But what does Go do internally to get you that <code>Regexp</code> struct? It's very interesting.</p> <p>When you call <code>regexp.Compile</code>, a number of things happen:</p> <ul> <li>first, <a href="http://golang.org/src/regexp/syntax/parse.go"><code>syntax.Parse</code></a> gets called. This parses a regular expression string s, controlled by the specified flags, and returns a regular expression parse tree. Next,</li> <li>the regular expression gets simplified by calling <a href="http://golang.org/src/regexp/syntax/simplify.go"><code>Simplify</code></a>, which returns a regexp equivalent to the given one but without counted repetitions and with various other simplifications, such as rewriting <code>/(?:a+)+/</code> to <code>/a+/</code>. And last,</li> <li><code>syntax.Compile</code> compiles the regexp into a program that can be executed. This step returns a <a href="http://golang.org/src/regexp/syntax/prog.go"><code>syntax.Prog</code></a>, which is a state machine, and it's what gets used to run match checks. It has a <code>String()</code> method that spits out its internals in human-readable format, and this is really useful in understanding regular expressions in Go.</li> </ul> <p>Here's a simple program that performs the steps described above, and prints out the resulting program (state machine):</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>package main </span><span> </span><span>import ( </span><span> &quot;fmt&quot; </span><span> &quot;regexp/syntax&quot; </span><span>) </span><span> </span><span>var flags = syntax.MatchNL | syntax.PerlX | syntax.UnicodeGroups </span><span> </span><span>func main() { </span><span> re, err := syntax.Parse(&quot;(NA|FE|HE)[CV]&quot;, flags) </span><span> if err != nil { </span><span> panic(err) </span><span> } </span><span> </span><span> re = re.Simplify() </span><span> prog, err := syntax.Compile(re) </span><span> if err != nil { </span><span> panic(err) </span><span> } </span><span> fmt.Println(prog) </span><span>} </span></code></pre> <p>(<a href="http://play.golang.org/p/3UhqsQ2Xyb">Go Playground</a>)</p> <p>Running this gives us the following output:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span> 0 fail </span><span> 1* cap 2 -&gt; 9 </span><span> 2 rune1 &quot;N&quot; -&gt; 3 </span><span> 3 rune1 &quot;A&quot; -&gt; 10 </span><span> 4 rune1 &quot;F&quot; -&gt; 5 </span><span> 5 rune1 &quot;E&quot; -&gt; 10 </span><span> 6 alt -&gt; 2, 4 </span><span> 7 rune1 &quot;H&quot; -&gt; 8 </span><span> 8 rune1 &quot;E&quot; -&gt; 10 </span><span> 9 alt -&gt; 6, 7 </span><span> 10 cap 3 -&gt; 11 </span><span> 11 rune &quot;CCVV&quot; -&gt; 12 </span><span> 12 match </span></code></pre> <p>This may seem cryptic at first, but it's actually not hard to understand. The output describes a state machine. The first number on each line is the ID of the node. The next word describes the type of node. It could be <code>cap</code>, which is short for <code>capture</code>, and corresponds to the start and end of match groups in the regex. <code>rune1</code> describes a node that exactly matches a specific rune. <code>alt</code> describes a node that merely exists to create a fork in the road: one path leads to the first argument, and the other to the second argument. And so on for different types of nodes. Every node has an output, described by the final number, which tells us the ID of the next node after this one. All in all, the above gives us a state machine diagram that looks more or less like this:</p> <p><a href="https://svbtleusercontent.com/5a7wrlp4oz6kng.png"><img src="https://svbtleusercontent.com/5a7wrlp4oz6kng_small.png" alt="regex.png" /></a></p> <p>You'll notice that I annotated the above state diagram with three steps. What are they? They are what we need to solve our initial problem: determinining whether a given character can be a solution to the regex at a particular step. If we want to know whether <code>'N'</code> satisfies the regular expression at position zero, we check whether <code>'N'</code> is a possible match at step one. If it is, <code>satisfiesAtPos</code> should return <code>true</code>, otherwise <code>false</code>. The problem is almost solved.</p> <p>I conveniently structured the state machine diagram so that rune matches fall on the same step for every position in the string. How do we do this in code? We can build up a list of runes at a particular step by stepping through the state machine, starting at the starting node. We start with a queue containing only the starting node, and we iterate until the queue is empty. If the next node in the queue is a rune node, we add it to our list of nodes at this step. If the node is not a rune, but rather an <code>alt</code> (alternative, or fork in the road) or <code>cap</code> (capture), we add this node's output nodes to the queue. Ideally, the queue should be a set, so that we can't add the same node to it twice. And that's how it's done; we can now complete our <code>satisfiesAtPos</code> function. In Go code, it looks like this:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>func satisfiesAtPos(expr string, r rune, pos int) bool { </span><span> re, err := syntax.Parse(expr, flags) </span><span> if err != nil { </span><span> panic(err) </span><span> } </span><span> </span><span> re = re.Simplify() </span><span> prog, err := syntax.Compile(re) </span><span> if err != nil { </span><span> panic(err) </span><span> } </span><span> </span><span> var el int </span><span> s := 0 // current step </span><span> step := []int{} // states in this step </span><span> queue := set([]int{prog.Start}) // queue of states to check </span><span> </span><span> for s &lt;= pos { </span><span> for len(queue) &gt; 0 { </span><span> el, queue = queue[0], queue[1:] </span><span> inst := prog.Inst[el] </span><span> switch inst.Op { </span><span> case syntax.InstAlt: </span><span> // alternative, so add both nodes to queue </span><span> queue.Add(int(inst.Out)) </span><span> queue.Add(int(inst.Arg)) </span><span> case syntax.InstCapture: </span><span> // capture group, just add next node to queue </span><span> queue.Add(int(inst.Out)) </span><span> case syntax.InstRune, syntax.InstRune1, syntax.InstRuneAny: </span><span> // rune, add instance to step </span><span> step = append(step, el) </span><span> } </span><span> } </span><span> </span><span> // range over the nodes we found in this step </span><span> for i := range step { </span><span> inst := prog.Inst[step[i]] </span><span> if s == pos &amp;&amp; inst.MatchRune(r) { </span><span> // if this is the step we want and </span><span> // the rune matches, return true </span><span> return true </span><span> } </span><span> // add rune&#39;s out to queue </span><span> queue = append(queue, int(inst.Out)) </span><span> } </span><span> step = []int{} </span><span> s++ </span><span> } </span><span> </span><span> return false </span><span>} </span></code></pre> <h3 id="putting-it-all-together">Putting it all together</h3> <p>Now that we have a working <code>satisfiesAtPos</code> function, the rest is relatively easy. For every cell, we guess a character and call <code>satisfiesAtPos</code> for both the horizontal and vertical regular expression. If the character satisfies both regexes, we go on to the next cell. If not, we guess the next character, and repeat the process. At the end of a row, we validate the row's regular expression, and if it doesn't match, we backtrack and keep making different guesses until it does. We also do the same for the columns. Even though we are still iterating over possible answers, we quickly find out when an answer cannot lead to a correct solution, and stop the search in its tracks. This reduces the search space considerably.</p> <p>If you are interested in the code for this function, feel free to check out the <a href="https://github.com/hermanschaaf/regex-crossword-solver">Github repo</a>.</p> <h3 id="caveats">Caveats</h3> <p>If you read the <a href="http://swtch.com/~rsc/regexp/regexp1.html">article by Russ Cox</a> I recommended earlier, you might have noticed the section on <em>backreferences</em>. Specifically, how they cannot be implemented in the way described in the article. This is why Go does not support regular expressions with backreferences at the moment. A "particularly clever" solution might one day combine the two algorithms so that the fast Thompson NFA is used when backreferences are not present, and only bring out backtracking when needed. Unfortunately, this also means that my current solution for regex crosswords, though simple, short and reasonably efficient, does not support backreferences. I'm still thinking of ways to remedy this without making the code horribly complicated. Let me know if you have any ideas!</p> <p>We also technically don't need to build the program every time inside the <code>satisfiesAtPos</code> function. A simple, but effective, optimization would be to build the program only once, and then compile the steps only once. This will surely make the solution much faster, but so far, for all the puzzles I tried, solutions were found in reasonable time, mostly within a few milliseconds.</p> <p>I have also not yet gotten around to adding support for puzzles with regular expressions on all four sides. I see no reason for it to be difficult, though.</p> <h3 id="grab-the-code">Grab the code</h3> <p>The code is <a href="https://github.com/hermanschaaf/regex-crossword-solver">available on Github</a>. It is not hard to hook up Selenium (or something similar), but let me not encourage such anti-social behavior :) Have fun!</p> Aquileo | Running the Go Race Detector with -cover2014-05-19T00:00:00+00:002014-05-19T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/running-the-go-race-detector-with-cover/<p>There are two test options that make testing in Go especially powerful: one is <code>-cover</code>, which generates test coverage reports, and the other is <code>-race</code>, which warns of possible race conditions in your code. If you haven't used them yet, you should. They are very useful tools, and both are covered well (so to speak) by official blog posts: <a href="http://blog.golang.org/cover">The cover story</a> and <a href="http://blog.golang.org/race-detector">Introducing the Race Detector</a>.</p> <p>A subtle problem arises, however, when you use these two together. Suppose we have this simple function that adds one a hundred times inside separate goroutines and returns the result of this summation:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>package coverrace </span><span> </span><span>func add100() int { </span><span> total := 0 </span><span> c := make(chan int, 1) </span><span> for i := 0; i &lt; 100; i++ { </span><span> go func(chan int) { </span><span> c &lt;- 1 </span><span> }(c) </span><span> } </span><span> for u := 0; u &lt; 100; u++ { </span><span> total += &lt;-c </span><span> } </span><span> return total </span><span>} </span></code></pre> <p>We can place the function in a file called coverrace.go, and write a test for it in coverrace_test.go. The test might look like this:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>package coverrace </span><span> </span><span>import &quot;testing&quot; </span><span> </span><span>func TestCoverRace(t *testing.T) { </span><span> got := add100() </span><span> if got != 100 { </span><span> t.Errorf(&quot;got %d, want %d&quot;, got, 100) </span><span> } </span><span>} </span></code></pre> <p>It just asserts that we get a 100 when calling the function, and raises an error if we don't. So far so good, let's run the test:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>$ go test </span><span>PASS </span><span>ok github.com/hermanschaaf/coverrace 0.005s </span></code></pre> <p>Test passed! Nice. Let's check out our coverage:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>$ go test -cover </span><span>PASS </span><span>coverage: 100.0% of statements </span><span>ok github.com/hermanschaaf/coverrace 0.005s </span></code></pre> <p>100% test coverage, not bad. Now let's use the race detector to make sure we didn't make any silly mistakes:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>$ go test -race </span><span>PASS </span><span>ok github.com/hermanschaaf/coverrace 1.053s </span></code></pre> <p>The test took a wee bit longer to run this time, but it looks like our implementation is OK as far as race conditions are concerned. Time to put it all together and get ready for uploading this to production! Let's just run everything one more time to be sure:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>$ go test -race -cover </span><span>================== </span><span>WARNING: DATA RACE </span><span>Write by goroutine 5: </span><span> github.com/hermanschaaf/coverrace.func·001() </span><span> github.com/hermanschaaf/coverrace/_test/coverrace.go:10 +0x45 </span><span> </span><span>Previous write by goroutine 4: </span><span> github.com/hermanschaaf/coverrace.func·001() </span><span> github.com/hermanschaaf/coverrace/_test/coverrace.go:10 +0x45 </span><span>... </span><span>================== </span><span>PASS </span><span>coverage: 100.0% of statements </span><span>Found 1 data race(s) </span><span>exit status 66 </span><span>FAIL github.com/hermanschaaf/coverrace 1.059s </span></code></pre> <p>Wait, what just happened? The code remained the same, but now we have a race condition! How is this possible?</p> <p>The tests are actually running on a copy of the source code, made by the cover tool to insert counting lines in code blocks. We can see this from the fact that the test errors are coming from a <code>_test</code> folder that we didn't create. The code, after adding the coverage lines, looks like this:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>package test </span><span> </span><span>func add100() int { </span><span> GoCover.Count[0] = 1 </span><span> total := 0 </span><span> c := make(chan int, 1) </span><span> for i := 0; i &lt; 100; i++ { </span><span> GoCover.Count[3] = 1 </span><span> go func(chan int) { </span><span> GoCover.Count[4] = 1 </span><span> c &lt;- 1 </span><span> }(c) </span><span> } </span><span> GoCover.Count[1] = 1 </span><span> for u := 0; u &lt; 100; u++ { </span><span> GoCover.Count[5] = 1 </span><span> total += &lt;-c </span><span> } </span><span> GoCover.Count[2] = 1 </span><span> return total </span><span>} </span></code></pre> <p>So cover added some extra lines to check off when any specific block of code is reached. It might be a bit old-school, but it works just fine. The only problem is that it is also setting a global variable inside a goroutine (<code>GoCover.Count[4] = 1</code>), and the race detector doesn't like that at all. What can we do if we want to run both cover and race detector at the same time?</p> <p>Luckily for us, there is a simple solution to this problem: we can run the tests using the special <code>-covermode=atomic</code> flag, like this:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>go test -race -covermode=atomic </span></code></pre> <p>Doing this, the generated code uses the <code>sync/atomic</code> package instead of setting a global variable, and it looks like this:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>import _cover_atomic_ &quot;sync/atomic&quot; </span><span> </span><span>func add100() int { </span><span> _cover_atomic_.AddUint32(&amp;GoCover.Count[0], 1) </span><span> total := 0 </span><span> c := make(chan int, 1) </span><span> for i := 0; i &lt; 100; i++ { </span><span> _cover_atomic_.AddUint32(&amp;GoCover.Count[3], 1) </span><span> go func(chan int) { </span><span> _cover_atomic_.AddUint32(&amp;GoCover.Count[4], 1) </span><span> c &lt;- 1 </span><span> }(c) </span><span> } </span><span> _cover_atomic_.AddUint32(&amp;GoCover.Count[1], 1) </span><span> for u := 0; u &lt; 100; u++ { </span><span> _cover_atomic_.AddUint32(&amp;GoCover.Count[5], 1) </span><span> total += &lt;-c </span><span> } </span><span> _cover_atomic_.AddUint32(&amp;GoCover.Count[2], 1) </span><span> return total </span><span>} </span><span> </span><span>var _ = _cover_atomic_.AddUint32 </span></code></pre> <p>, which is safe from race conditions, as shown by the new output of running the tests with this flag:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>$ go test -race -covermode=atomic </span><span>PASS </span><span>coverage: 100.0% of statements </span><span>ok github.com/hermanschaaf/coverrace 1.055s </span><span> </span></code></pre> <p>Crisis averted!</p> <p><strong>Protip</strong>: when running <code>go test</code> with both <code>-cover</code> and <code>-race</code>, also add the <code>-covermode=atomic</code> flag - it might just save you an hour or two of unnecessary race condition-hunting.</p> <p>If you'd like to play with the source code of this post, it's <a href="https://github.com/hermanschaaf/coverrace">on Github</a>.</p> <p><strong>Update</strong>: Good news, everyone! <a href="http://twitter.com/shawnps">@shawnps</a> pointed out that from Go 1.3 the atomic mode will be enabled by default when -cover is run with -race, so this will not be an issue for much longer :)</p> Aquileo | Efficient String Concatenation in Go2014-05-03T00:00:00+00:002014-05-03T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/efficient-string-concatenation-in-go/<p>In this article I investigate the computational performance of various string concatenation methods in the Go programming language.</p> <p>To evaluate the options, I prepared some typical Go benchmarks using the Go <code>testing</code> package. A benchmark looks something like this:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>func BenchmarkBufferString(b *testing.B, numConcat int) { </span><span> var ns string </span><span> for i := 0; i &lt; b.N; i++ { </span><span> next := nextString() </span><span> buffer := bytes.NewBufferString(&quot;&quot;) </span><span> for u := 0; u &lt; numConcat; u++ { </span><span> buffer.WriteString(next()) </span><span> } </span><span> ns = buffer.String() </span><span> } </span><span> global = ns </span><span>} </span></code></pre> <p>For the purposes of these benchmarks, I imagined having a process that returns string segments one by one, and these segments need to be concatenated to form one string. To represent such a process, I created a simple iterator that returns the string version of its current index on each call:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>// nextString is an iterator we use to represent a process </span><span>// that returns strings that we want to concatenate in order. </span><span>func nextString() func() string { </span><span> n := 0 </span><span> // closure captures variable n </span><span> return func() string { </span><span> n += 1 </span><span> return strconv.Itoa(n) </span><span> } </span><span>} </span></code></pre> <p>Every method we evaluate will call this function in a loop so that the fully concatenated string looks something like <code>0123456789101112131415...</code>. There is function call overhead to take into account in this, but since all the benchmarks will have the same overhead, the comparison is still fair. Now that we have the basic setup for the benchmark, let's see what the methods are we'll be evaluating.</p> <h1 id="method-1-naive-appending-with">Method 1: Naive Appending with +=</h1> <p>This is the most obvious approach to the problem. Use the concatenate operator (+=) to append each segment to the string. The thing to know about this is that strings are immutable in Go - each time a string is assigned to a variable a new address is assigned in memory to represent the new value. This is different from languages like C++ and BASIC, where a string variable can be modified in place. In Go, each time you append to the end of a string, it must create a new string and copy the contents of both the existing string and the appended string into it. As the strings you are manipulating become large this process becomes increasingly slow. In fact, this method of naive appending is O(N<sup>2</sup>).</p> <h1 id="method-2-strings-join">Method 2: strings.Join</h1> <p>The Go <code>strings</code> standard library package provides a <a href="http://golang.org/pkg/strings/#Join"><code>Join</code></a> function that takes a slice of strings and concatenates them. In our case, this requires first building a slice of strings and then calling the function on the slice. Building a slice takes time if you don't already have one, and we therefore count this as part of the time taken to perform the concatenation in our benchmarks. The final concatenation will be O(N), but building the slice is O(N) (average) and uses O(N) memory.</p> <p>Here is an example of using <code>strings.Join</code>:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>s := []string{} </span><span>for i := 0; i &lt; 1000; i++ { </span><span> s = append(s, &quot;a&quot;) </span><span>} </span><span>fmt.Println(strings.Join(s, &quot;&quot;)) </span></code></pre> <p><a href="http://play.golang.org/p/DUrUJbfNqY">See full code on the Go Playground</a></p> <h1 id="method-3-bytes-buffer">Method 3: bytes.Buffer</h1> <p>The last method we'll be evaluating is using the <a href="http://golang.org/pkg/bytes/#Buffer">Buffer type</a> from the bytes package. <code>bytes.Buffer</code> implements io.Writer, and can concatenate strings in O(N) time without requiring us to build a new slice.</p> <p>Here is an example of using <code>bytes.Buffer</code>:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>var buffer bytes.Buffer </span><span>for i := 0; i &lt; 1000; i++ { </span><span> buffer.WriteString(&quot;a&quot;) </span><span>} </span><span>fmt.Println(buffer.String()) </span></code></pre> <p><a href="http://play.golang.org/p/2PIzcNHOzl">See full code on the Go Playground</a></p> <p>Now that we've defined the methods we want to test, let's proceed to run the benchmarks. I'd like to skip right to the end and start with the winning approach. Then we'll work back to see how and when this method is best.</p> <h1 id="and-the-winner-is">And the Winner Is...</h1> <p><strong>Method #3</strong> (<code>bytes.Buffer</code>) is the winner! But not by far, and not in all cases. Here is a graph of how the methods compare in the case with a 10000 strings with a total size of 37kB (small strings of &lt;= 5 characters):</p> <p><a href="https://svbtleusercontent.com/rlmmrxjtthkg.png"><img src="https://svbtleusercontent.com/rlmmrxjtthkg_small.png" alt="Comparison of String Concat Methods in Go" /></a></p> <p>It shows how <code>bytes.Buffer</code> is the winner by quite a large margin in this case. Let's see how the methods compare when the strings are a bit longer, at a total size of 244kB (25 characters each):</p> <p><a href="https://svbtleusercontent.com/dfqz7oazqu0ddg.png"><img src="https://svbtleusercontent.com/dfqz7oazqu0ddg_small.png" alt="String Concat Methods in Go (244kB)" /></a></p> <p>With longer strings <code>strings.Join</code> doesn't perform so badly, but it's still not as good as using <code>bytes.Buffer</code>.</p> <p>I also ran benchmarks on small numbers of concatenations, and in that case naive concatenation gets the job done slightly faster than the other methods, so it's still fine to use for small numbers of concatenations and small segments.</p> <p>Lastly, how big of a factor does building a string slice play in Method #2? We can exclude slice building from the <code>strings.Join</code> benchmark, but make it still call <code>nextString</code> on every iteration so that the tests remain fair. In this case, the results look like this:</p> <p><a href="https://svbtleusercontent.com/gk1cu1j9sbk5ew.png"><img src="https://svbtleusercontent.com/gk1cu1j9sbk5ew_small.png" alt="String Concat Methods, Not Counting Slice-building" /></a></p> <p>When we don't count the time it takes to build a string slice, <code>strings.Join</code> edges out <code>bytes.Buffer</code> and rules supreme.</p> <h1 id="in-summary">In Summary</h1> <p>In summary, for very few concatenations of short strings (fewer than hundred, length shorter than 10), using Method #1, naive string appending, is just fine and can actually perform slightly better. For more heavy-duty cases where efficiency is important, Method #3, <code>bytes.Buffer</code> is the best choice out of the methods evaluated. That said, <code>strings.Join</code> is a good choice when you already have a string slice that just needs to be concatenated into one string.</p> <h1 id="get-the-code">Get the Code</h1> <p>Below are the raw results of running <code>go test -bench.</code> for the different methods and different number of concatenations on my machine (Go 1.2 on Ubuntu 64bit, Intel® Core™ i7-3667U CPU @ 2.00GHz):</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>BenchmarkNaiveConcat10 500000 6403 ns/op </span><span>BenchmarkNaiveConcat100 20000 77001 ns/op </span><span>BenchmarkNaiveConcat1000 500 3105361 ns/op </span><span>BenchmarkNaiveConcat10000 5 236872134 ns/op </span><span>BenchmarkJoin10 200000 8129 ns/op </span><span>BenchmarkJoin100 50000 51891 ns/op </span><span>BenchmarkJoin1000 5000 465179 ns/op </span><span>BenchmarkJoin10000 500 5911317 ns/op </span><span>BenchmarkBufferString10 200000 7688 ns/op </span><span>BenchmarkBufferString100 50000 45027 ns/op </span><span>BenchmarkBufferString1000 5000 398311 ns/op </span><span>BenchmarkBufferString10000 500 3702906 ns/op </span></code></pre> <p>You can play around with the benchmarks, run them yourself and check my implementation <a href="http://github.com/hermanschaaf/go-string-concat-benchmarks">on Github</a>. A typical benchmark looks like this:</p> <pre style="background-color:#eff1f5;color:#4f5b66;"><code><span>var global string </span><span> </span><span>// benchmarkNaiveConcat provides a benchmark for basic built-in </span><span>// Go string concatenation. Because strings are immutable in Go, </span><span>// it performs the worst of the tested methods. The time taken to </span><span>// set up the array that is appended is not counted towards the </span><span>// time for naive concatenation. </span><span>func benchmarkNaiveConcat(b *testing.B, numConcat int) { </span><span> var ns string </span><span> for i := 0; i &lt; b.N; i++ { </span><span> next := nextString() </span><span> ns = &quot;&quot; </span><span> for u := 0; u &lt; numConcat; u++ { </span><span> ns += next() </span><span> } </span><span> } </span><span> // we assign to a global variable to make sure compiler </span><span> // or runtime optimizations don&#39;t skip over the operations </span><span> // we were benchmarking. This might be unnecessary, but it&#39;s </span><span> // safer. </span><span> global = ns </span><span>} </span></code></pre> Aquileo | Why is Golang popular in China?2014-04-16T00:00:00+00:002014-04-16T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/why-is-go-popular-in-china/<p><a href="https://svbtleusercontent.com/51i27mwtbnxeg.png"><img src="https://svbtleusercontent.com/51i27mwtbnxeg_small.png" alt="Search for &quot;golang&quot; on Google Trends" /></a></p> <p>Earlier this week I wrote a post titled <a href="https://herman.svbtle.com/the-popularity-of-go">The Popularity of Go</a>. The post itself turned out to be quite popular, and I received a lot of great feedback from the Go community (thanks!). One thing I brought up in the post, and that a lot of folks commented on, was the apparent popularity of Go in China according to Google Trends. The graph above shows the searches for "golang" by country on Google Trends. Impressive, right? So many more searches for "golang" in China than anywhere else in the world. Like many others, I too wanted to know why. I'm not much of an investigative journalist, but I decided to do some of my own digging anyway. Is Go really that popular in China, or are we seeing skewed data? And if the data is skewed, why, and how much?</p> <p>One redditer suggested that, perhaps, "golang" is in fact a word in a dialect of Chinese. Being able to speak and read Mandarin Chinese myself, I was intrigued by this idea - it seemed plausible, at least. So I hopped onto <a href="http://www.baidu.com/#wd=golang&amp;rsv_bp=0&amp;tn=baidu&amp;rsv_spt=3&amp;ie=utf-8&amp;rsv_sug3=3&amp;rsv_sug2=0&amp;inputT=821">Baidu.com</a>, and did a search for "golang" (Baidu is, for those of you who don't know, by far the <a href="http://www.chinainternetwatch.com/category/search-engine/">most popular search engine in China</a>). The first one hundred results all related to the Go programming language in some way. So perhaps it doesn't have a different meaning in China after all.</p> <p>But we don't need to rely on anecdotal search results for evidence, we can confirm that Chinese searchers genuinely are interested in Go, the programming language, by seeing what percentage of visits to golang.org are reported to be from China on Alexa.com. This is <a href="http://www.alexa.com/siteinfo/golang.org">what we find</a>:</p> <p><a href="https://svbtleusercontent.com/qly9wp71cldfg.png"><img src="https://svbtleusercontent.com/qly9wp71cldfg_small.png" alt="Golang.org visitor breakdown by country" /></a></p> <p>The US weighs in at 18.9% of the total visitors to golang.org, with China coming a reasonably close second, at 12.1%. This is already enlightening: the Google Trends graph made it look like the US interest in "golang" was nowhere close to that of China, but here we see that more visits to the main Go-related website come from the US than from China.</p> <p>But wait a minute. Is golang.org really the main Go-related website in China? Is it even available in the Chinese language? Whoops, doesn't look like it. Even on Baidu it's the only English result on the page:</p> <p><a href="https://svbtleusercontent.com/4ygyehpt78blw.png"><img src="https://svbtleusercontent.com/4ygyehpt78blw_small.png" alt="baidu.png" /></a></p> <p>This makes the argument much weaker: maybe Chinese gophers are using a reference site in their native tongue, not golang.org, and that would explain why golang.org shows a smaller percentage of Chinese users. Sounds reasonable. So let's dig deeper, and find out where Chinese gophers <em>do</em> hang out.</p> <p>There are a couple of popular Chinese sites related to Go. The first is <a href="http://studygolang.com">studygolang.com</a>. Another is <a href="http://golangtc.com/">Golang中国</a>. Another is <a href="http://golang.usr.cc/">golang.usr.cc</a>. I'm sure there are more, but as far as I can see those are the most prominent sites that are only about Go, and not about programming in general. Let's see how these stack up in terms of rankings on Alexa, and how they compare to golang.org, as of this writing:</p> <table><thead><tr><th>Site</th><th>Rank in China</th></tr></thead><tbody> <tr><td><a href="http://studygolang.com">studygolang.com</a></td><td>6,928</td></tr> <tr><td><a href="http://golang.org">golang.org</a></td><td>14,932</td></tr> <tr><td><a href="http://golangtc.com/">golangtc.com</a></td><td>17,606</td></tr> <tr><td><a href="http://golang.usr.cc/">golang.usr.cc</a></td><td>51,897</td></tr> </tbody></table> <p>So studygolang.com seems to be the clear leader in terms of visitors, and is even more popular than <a href="http://golang.org">golang.org</a>, as we suspected some site might be.</p> <p>Now that we know which sites are popular in China, can we use this information to glean something about how popular Go is there in general, compared to the rest of the world? I think we can. Below are tables containing the most popular keywords to find the different Go-related websites. See if you can spot the odd one out:</p> <p><a href="https://svbtleusercontent.com/vtbuxjg0ziwoga.png"><img src="https://svbtleusercontent.com/vtbuxjg0ziwoga_small.png" alt="Most Popular Keywords for different Go-related sites" /></a></p> <p>If you look at the top search term for each site, you'll notice that <a href="http://golang.org">golang.org</a> gets disproportionately many of its search visitors from the keword "Go" (74.30%), whereas the other popular sites in China have as their top keyword "Golang", some as their most popular term by far and others only by a little bit (13.29%, 80.67% and 12.12%), but with the term "Go" never featuring for any of the Chinese sites. golang.org gets ~25 times more visitors from the search term "go" than it does from the term "golang". It is therefore reasonable to estimate that when we were looking at the Google Trends graph for "golang", we were really seeing only about 4% of the full picture. At the same time, Chinese sites, for whatever reason, get very few hits from "go", and most of their hits come from "golang". It is likely that Chinese users are using "golang" as a search term more often - even more often than other countries' users - and this in turn results in us seeing the Google Trends data showing China as the origin of the most searches for "golang".</p> <h4 id="so-is-go-popular-in-china-or-not">So Is Go Popular in China or Not?</h4> <p>So now we have seen why, at least to some extent, there are more searches for "golang" in China than elsewhere. But we are yet to find out whether Go really <em>is</em> popular in China. Like everything else so far, this too is difficult to know for sure. But we can do some back-of-the-napkin math to see where the biggest online Go community lives: China or the US?</p> <p>We saw earlier that 18.9% of golang.org's visitors come from the US, while 12.1% come from China. We also saw that golang.org is ranked 14,932th in China, and studygolang.com 6,928th. Assuming that a halving in rank constitutes a doubling in monthly visitors, we can guess, back-of-the-napkin-style, that studygolang.com receives about twice as many Chinese visitors as golang.org. That would translate to 24.2% of golang.org's total number of visitors that visit studygolang.com every month - more than the monthly number of US visitors to golang.org.</p> <p>Now, there are so many assumptions being made here, both stated and unstated, that it would be most unwise to take any final numbers seriously. But it looks like the conclusion is nevertheless unavoidable: even conservatively, it is very likely that there are at least as many Go coders in China as there are in the US. Sure, China has a much bigger population too, but it's still a surprising result considering that the main Go site, golang.org, does not have a Chinese version available (yet).</p> <p>So how did Chinese gophers hear about Go, and how did it spread so quickly in China? To be frank, I don't know. But I'd like to know, and they say that the quickest way to get to the correct answer on the Internet is not to ask a question, but to post an incorrect answer. In that spirit, I'll venture a guess.</p> <p>A Chinese blogger called Liigo recently wrote an article on why he <a href="http://blog.csdn.net/liigo/article/details/23699459">decided to abandon Go</a> (text in Chinese). He specifically mentions why he, and other admirers like him, first showed interest in Go back in 2009: because it was "made by Google". It seems like that would be as a good a reason in China as anywhere else, but maybe it played a big role in the adoption of Go in China, as opposed to, say, Rust or Node.js. He also goes on to mention how he was initially excited to join the thriving Go community. We can only assume that he is referring to the core Go developers, but perhaps he is alluding to the thriving Go community in China as well.</p> <p>Another popular post on Quora-like Chinese Q&amp;A site zhihu.com asks "<a href="http://www.zhihu.com/question/21409296">Why should I use Go?</a>", and it has some interesting answers. It can be directly compiled into machine code, it's easy to learn, it has a simple concurrency model, is suitable for server programming, etc. Basically, all the reasons we know and love. But nothing that stands out as a particularly unique reason to China.</p> <p>From the scarce evidence available, there is one conclusion to be drawn: Go is popular in China for the same reasons that it is popular in the US, with one extra strengthening factor being a high reverence in China for things made by Google. We can demonstrate this last point by looking at the popularity of other Google technologies in China. Here's Google App Engine's world map on Google Trends:</p> <p><a href="https://svbtleusercontent.com/wohroem4dxa4ga.png"><img src="https://svbtleusercontent.com/wohroem4dxa4ga_small.png" alt="Google App Engine Popularity in China" /></a></p> <p>And here's the same graph for Node.js, for comparison:</p> <p><a href="https://svbtleusercontent.com/dahtzlwrl7js8w.png"><img src="https://svbtleusercontent.com/dahtzlwrl7js8w_small.png" alt="Node.js Popularity in China" /></a></p> <p>Google App Engine is also way more popular in China than anywhere else, and the same does not go for Node.js, so we can discount population size distortion.</p> <p>tl;dr? From an outsider's perspective, it seems like Google technologies are popular in China for no reason other than that they were made by Google. I'd be very happy to be shown wrong, or to hear from Chinese gophers themselves!</p> <p><strong>Update</strong>: Dave Cheney <a href="http://www.reddit.com/r/golang/comments/23c7y0/why_is_golang_popular_in_china/cgvkppp">pointed out on Reddit</a> that he is under the impression that Go is popular in China for its usefulness in writing programs for rooted Android phones. Very interesting point, and I tend to believe what Dave says about Go. Seems like it only took a couple of hours for me to be proven wrong on the Internet, and I'm happy about that. Any more theories (or better, proof) out there? Let me know! <a href="https://twitter.com/ironzeb">@ironzeb</a></p> <p>Also on Reddit, <a href="http://www.reddit.com/r/golang/comments/23c7y0/why_is_golang_popular_in_china/cgvlwzy">zx9597446 pointed out</a> that golang.org is often blocked by the Great Firewall, and that many Chinese gophers are forced to access resources through a proxy. There might be even more gophers in China than it seems!</p> Aquileo | The Popularity of Go2014-04-14T00:00:00+00:002014-04-14T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/the-popularity-of-go/<p><a href="https://svbtleusercontent.com/qrjcowoaglb2a.png"><img src="https://svbtleusercontent.com/qrjcowoaglb2a_small.png" alt="go_trend1.png" /></a></p> <p>When you look at the Google Trends graph above, it would seem like the Go programming language, also known as Golang, is on its way to big things. But let's not get ahead of ourselves, Go fans. Let's first have a cup of java to put things into perspective:</p> <p><a href="https://svbtleusercontent.com/guxh6ahaxfntya.png"><img src="https://svbtleusercontent.com/guxh6ahaxfntya_small.png" alt="golang_trend2.png" /></a></p> <p>Golang's popularity as a search term might be growing faster than bamboo in springtime, but it's still dwarfed by the massive overgrowth that is the Java programming language. But interestingly, we can see in the graph how dramatically Java's popularity has fallen in recent years. Maybe it has something to do with the worrying decline in programming interest?</p> <p><a href="https://svbtleusercontent.com/m9kbr4pg4p8kaa.png"><img src="https://svbtleusercontent.com/m9kbr4pg4p8kaa_small.png" alt="go_trends3.png" /></a></p> <p>Hmm. Not sure what to make of that, considering my career choices. Anyway, while Go's popularity is on the rise, it's worth pondering whether it will ever grow to the heights that Java once enjoyed. It doesn't look likely to happen any time soon.</p> <p>Another interesting tidbit is that Go is enjoying surprising popularity in China, as shown by the fact that most searches on Google related to Golang originate from there:</p> <p><a href="https://svbtleusercontent.com/3vdpocobw0xbhg.png"><img src="https://svbtleusercontent.com/3vdpocobw0xbhg_small.png" alt="golang_trend4.png" /></a></p> <p>It makes at least some sense, since a few interesting Go libraries have come from Chinese developers, like <a href="http://beego.me/">beego</a> and <a href="https://github.com/jinzhu/gorm">gorm</a>.</p> <p>What does Go's future have in store? It's hard to tell. But it sure is an exciting time to be involved.</p> <p><em>Disclaimer: These trends are by nature wildly inaccurate. I share them as food for thought. Please don't use it for anything important. And if you do, it's on you :)</em></p> Aquileo | Rugby2014-01-19T00:00:00+00:002014-01-19T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/posts/an-algorithmic-approach-to-rugby/<p>Rugby is a sport close to my heart. I have always enjoyed watching, playing and discussing this fast-paced game of grit, strategy and strength. I am, however, also an avid fan of computer science, and my upper body strength is not quite what it used to be. I believe that no topic, no matter how interesting, can not be made even more interesting through the use of math, science and algorithms, without any need for upper body strength. Indeed, I will prove that rugby is no exception to this rule.</p> <p>For the uninitiated, in rugby, there are four types of scores: penalty goal (3 points), drop goal (3 points), try (5 points) and converted try (7 points). It is easy to show that from this, all points totals are possible, except for 1, 2 and 4. In this post, I’ll be answering a bunch of hypothetical questions that can be asked about these scores, and applying algorithms from computer science to do it as efficiently as possible.</p> <p>To kick off, let’s find out what the minimum number of scoring opportunities a team needs to achieve a specific points total. For example, for a team to reach a score of 8, the minimum number of scoring opportunities they need is two, because they need one try and one penalty or drop goal, and there are no other ways to reach that points total.</p> <p>We can tackle this problem with a dynamic programming approach. Dynamic programming is a technique by which optimal solutions are first found for smaller sub-problems, and a recurrence relationship then used to calculate the solution for the bigger problems. In this case, we can start by calculating the minimum number of scoring opportunities needed for smaller scores, and use these solutions to build up to bigger scores.</p> <p>We have already noted that all totals are possible except 1, 2 and 4. We can therefore initialize some data with the minimum number of scoring opportunities needed for certain totals:</p> <ul> <li>0 points: 0 opportunities needed</li> <li>1 point: impossible</li> <li>2 points: impossible</li> <li>3 points: 1 scoring opportunity (drop kick or penalty kick)</li> <li>4 points: impossible</li> <li>5 points: 1 scoring opportunity (try)</li> </ul> <p>We can now continue and note that for any total, the minimum number of opportunities must be equal to the minimum for a previous total, plus one extra scoring opportunity needed to reach this new total. For example, to reach 8, the minimum is one extra opportunity (a drop kick, 3 points) plus the minimum for achieving 8-3=5, which happens to be 1. The minimum for 8 is therefore 2. Being the particularly mathematically-inclined rugby enthusiasts that we are, we prefer things in equation form:</p> <img src="/images/rugby/rugby-equation.png" alt="f(x) = min_{\, \forall\, s\textup{ in } scores } \ \begin{cases}f(x-s)+1 &amp; \textup{if }x-s\textup{ is possible}\\infinity &amp; otherwise\end{cases}"> <p>This is where you can see the recurrence relationship clearly; to calculate the solution for a new total points <code>x</code>, for every score <code>s</code> (either 3, 5 or 7), we check the previous minimum totals calculated at <code>x-s</code> and see which one is the minimum needed. If we were to now calculate the minimum for all totals from 1 up to <code>n</code>, we can create a table of minimum scoring opportunities needed in O(n) time and O(n) space. Here is this table up to 20 points:</p> <table> <tr><th>Total Points</th><th>Minimum Scoring Opportunities</th></tr> <tr><td> 0 </td><td> 0 </td></tr> <tr><td> 1 </td><td> Impossible </td></tr> <tr><td> 2 </td><td> Impossible </td></tr> <tr><td> 3 </td><td> 1 </td></tr> <tr><td> 4 </td><td> Impossible </td></tr> <tr><td> 5 </td><td> 1 </td></tr> <tr><td> 6 </td><td> 2 </td></tr> <tr><td> 7 </td><td> 1 </td></tr> <tr><td> 8 </td><td> 2 </td></tr> <tr><td> 9 </td><td> 3 </td></tr> <tr><td> 10 </td><td> 2 </td></tr> <tr><td> 11 </td><td> 3 </td></tr> <tr><td> 12 </td><td> 2 </td></tr> <tr><td> 13 </td><td> 3 </td></tr> <tr><td> 14 </td><td> 2 </td></tr> <tr><td> 15 </td><td> 3 </td></tr> <tr><td> 16 </td><td> 4 </td></tr> <tr><td> 17 </td><td> 3 </td></tr> <tr><td> 18 </td><td> 4 </td></tr> <tr><td> 19 </td><td> 3 </td></tr> <tr><td> 20 </td><td> 4 </td></tr> </table> <p><a href="https://gist.github.com/hermanschaaf/8505353">Full table</a></p> <p>Putting our algorithm in use, we can see that New Zealand needed a minimum of 21 scoring opportunities in their record-breaking game against Japan in the 1995 World Cup to reach their final points total of 145. This makes it all the more remarkable that they did, indeed, score 21 times, achieving maximum efficiency, something we programmers can surely appreciate (Simon Culhane managed to convert all but one of the 21 tries in that game).</p> <p>Okay, that was fun! But we’re only warming up. In that silent moment huddling down for a scrum, have you ever wondered, for a given points total, what are all the possible ways was that the game could have played out? I know I have.</p> <p>Let’s extend our algorithm a bit to answer that question, and find all scoring combinations to achieve any one total score. This is very similar to what we were seeking before, except that now, instead of looking for the minimum number of scoring opportunities, we want to find all the possible combinations of scoring opportunities. For our purposes, it’s okay to assume that a penalty conversion and a drop kick conversion is one and the same, as they amount to the same score of 3.</p> <p>We can again use a dynamic programming approach, but this time we have to be a little more careful: if we were to simply add the combinations of previous totals to obtain the new total, we might count the same combination twice. For example, for a total of 3 there is only one combination: one penalty (or drop) kick. For a total of 5, there is also only combination: one try. Following our earlier algorithm, for a total of 8, then, we could look at the score 8-5=3, and find one combination. Next we look at 8-3=5, finding one combination again. We then look at 8-7=1, which has no combinations. So the total combinations is two. Right? Wrong! In using this dynamic programming shortcut, we accidentally counted the same combination twice: 3+5, and 5+3, which we consider to be the same thing. A total of 8 points can have only one combination, a try and a penalty kick. Therefore, we need to keep track of the combinations we found before, and ensure that any new combination we add is unique. Luckily this isn&rsquo;t so bad, and in running our algorithm we can optimize memory by only keeping track of the combinations we found at 4 positions: <code>i</code>, the current total, <code>i-3</code>, <code>i-5</code> and <code>i-7</code>. From these combinations we can build all possible unique combinations for <code>i</code>. You can see an implementation of this in Go by reading the <a href="https://web.archive.org/web/20160616201637/http://github.com/hermanschaaf/rugby">source code</a> for this post.</p> <p>With the results calculated, let’s see the table of combinations for all the scores up to 20!</p> <table> <tr><th>Score</th><th>Kicks</th><th>Tries</th><th>Tries + Conversions</th></tr> <tr><td>0</td><td>0</td><td>0</td><td>0</td></tr> <tr><td>3</td><td>1</td><td>0</td><td>0</td></tr> <tr><td>5</td><td>0</td><td>1</td><td>0</td></tr> <tr><td>6</td><td>2</td><td>0</td><td>0</td></tr> <tr><td>7</td><td>0</td><td>0</td><td>1</td></tr> <tr><td>8</td><td>1</td><td>1</td><td>0</td></tr> <tr><td>9</td><td>3</td><td>0</td><td>0</td></tr> <tr><td>10</td><td>1</td><td>0</td><td>1</td></tr> <tr><td></td><td>0</td><td>2</td><td>0</td></tr> <tr><td>11</td><td>2</td><td>1</td><td>0</td></tr> <tr><td>12</td><td>4</td><td>0</td><td>0</td></tr> <tr><td></td><td>0</td><td>1</td><td>1</td></tr> <tr><td>13</td><td>2</td><td>0</td><td>1</td></tr> <tr><td></td><td>1</td><td>2</td><td>0</td></tr> <tr><td>14</td><td>3</td><td>1</td><td>0</td></tr> <tr><td></td><td>0</td><td>0</td><td>2</td></tr> <tr><td>15</td><td>5</td><td>0</td><td>0</td></tr> <tr><td></td><td>1</td><td>1</td><td>1</td></tr> <tr><td></td><td>0</td><td>3</td><td>0</td></tr> <tr><td>16</td><td>3</td><td>0</td><td>1</td></tr> <tr><td></td><td>2</td><td>2</td><td>0</td></tr> <tr><td>17</td><td>4</td><td>1</td><td>0</td></tr> <tr><td></td><td>1</td><td>0</td><td>2</td></tr> <tr><td></td><td>0</td><td>2</td><td>1</td></tr> <tr><td>18</td><td>6</td><td>0</td><td>0</td></tr> <tr><td></td><td>2</td><td>1</td><td>1</td></tr> <tr><td></td><td>1</td><td>3</td><td>0</td></tr> <tr><td>19</td><td>4</td><td>0</td><td>1</td></tr> <tr><td></td><td>3</td><td>2</td><td>0</td></tr> <tr><td></td><td>0</td><td>1</td><td>2</td></tr> <tr><td>20</td><td>5</td><td>1</td><td>0</td></tr> <tr><td></td><td>2</td><td>0</td><td>2</td></tr> <tr><td></td><td>1</td><td>2</td><td>1</td></tr> <tr><td></td><td>0</td><td>4</td><td>0</td></tr> </table> <p><a href="https://gist.github.com/hermanschaaf/8505510">Full table</a></p> <p>From this table we can see that for a points total of 20, four different combinations are possible: 5 kicks and 1 try, 2 kicks and 2 converted tries, 1 kick, 2 tries and 1 converted try, or 4 tries. If we let the program run up to 145, the same highest score in a test match by New Zealand we saw earlier, we see that there were 119 different ways that the match could have played out so that New Zealand still ended on 145.</p> <p>And we haven’t even talked about permutations! How many ways could that famous game have played out to reach the same 145 points total? In other words, in how many ways can we order each of the combinations to get the same result? If we have a game with 8 points total, a try and a penalty kick, that game could have played out in two ways: either first the try and then the penalty kick, or first the penalty kick, then the try. We can easily adapt our solution from the previous question to answer this for any total, but the numbers will grow very big very fast. I’ll leave this as an “exercise for the reader” ;)</p> <h2>What have we learnt?</h2> <ul> <li><p>Algorithms make everything more interesting.</p></li> <li><p>To score a total of 145, you need to score at least 21 times.</p></li> <li><p>The 1995 New Zealand World Cup Team were both formidable and efficient, and would make good computer scientists.</p></li> <li><p>Dynamic programming is a useful technique in your programming toolkit.</p></li> </ul> <p>You can find the <a href="https://github.com/hermanschaaf/rugby">full source code for this post here</a>.</p> Aquileo | Integer Square Root Implementation2013-12-23T00:00:00+00:002013-12-23T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/posts/integer-square-root-implementation/<h2 id="problem">Problem</h2> <blockquote> <p>Find the square root of a given number rounded down to the nearest integer, without using the sqrt function. For example, squareRoot of 9 should return 3, and squareRoot of 17 should return 4.</p> </blockquote> <h2 id="solution">Solution</h2> <p>Let us consider what it means for a number <code>x</code> to be the square root of a number <code>s</code>. If <code>x</code> is the square root of <code>s</code>, <code>x^2</code> is equal to <code>s</code>. Therefore, stated differently, the problem is to, given a number <code>s</code>, find a number <code>x</code> so that <code>x</code> is the biggest number for which<code>x^2</code> is either equal or smaller than <code>s</code>.</p> <p>We may consider this to be a search problem. We have a number <code>s</code>, and we have to find a number <code>0 &lt;= x &lt;= s</code> that satisfies certain conditions. Since the search space is the number line, which is already sorted, binary search is well-suited. We start our search with the full search space, with a number <code>low := 0</code>, and a number <code>high := s</code>. We then guess <code>x</code> to be in the middle of <code>high</code> and <code>low</code>, at <code>(high + low) / 2</code>. If <code>x^2</code> is larger than <code>s</code> we narrow our search space by setting <code>high</code> to a lower value. If <code>x^2</code> is smaller than <code>s</code> we do the same by setting <code>low</code> to a higher value. This is just like a normal binary search, except that instead of an array of integers, we are searching all integers between <code>0</code> and <code>s</code>.</p> <p>The fact that we are only required to find an integer square root means that we do not need floating point calculations, which boosts efficiency. However, because the problem statement requires the square root be rounded down, we need to check two conditions:</p> <ul> <li>that the square of the <code>x</code> we have found is either smaller or equal to <code>v</code>, and</li> <li>that the square of <code>x+1</code> is larger than <code>v</code>.</li> </ul> <p>When these two conditions are met, we have found the square root of <code>v</code>, rounded down to the nearest integer.</p> <h3>Solution</h3> <p>Here is an implementation of the algorithm, written in Go:</p> <pre data-lang="go" style="background-color:#eff1f5;color:#4f5b66;" class="language-go "><code class="language-go" data-lang="go"><span style="color:#b48ead;">func </span><span style="color:#8fa1b3;">SquareRoot</span><span>(</span><span style="color:#bf616a;">v </span><span style="color:#b48ead;">int</span><span>) (</span><span style="color:#bf616a;">i </span><span style="color:#b48ead;">int</span><span>, </span><span style="color:#bf616a;">err </span><span style="color:#b48ead;">error</span><span>) { </span><span> </span><span style="color:#a7adba;">// use Newton&#39;s method to narrow in on the nearest </span><span> </span><span style="color:#a7adba;">// integer x where x*x is closest to v. </span><span> </span><span style="color:#b48ead;">var </span><span style="color:#bf616a;">low</span><span>, </span><span style="color:#bf616a;">high </span><span style="color:#b48ead;">int </span><span>= </span><span style="color:#d08770;">0</span><span>, </span><span style="color:#bf616a;">v </span><span> </span><span> </span><span style="color:#a7adba;">// we have special cases for 0 and 1 </span><span> </span><span style="color:#b48ead;">if </span><span style="color:#bf616a;">v </span><span>== </span><span style="color:#d08770;">0 </span><span>|| </span><span style="color:#bf616a;">v </span><span>== </span><span style="color:#d08770;">1 </span><span>{ </span><span> </span><span style="color:#b48ead;">return </span><span style="color:#bf616a;">v</span><span>, </span><span style="color:#d08770;">nil </span><span> } </span><span> </span><span> </span><span style="color:#a7adba;">// if v is negative, return an error </span><span> </span><span style="color:#b48ead;">if </span><span style="color:#bf616a;">v </span><span>&lt; </span><span style="color:#d08770;">0 </span><span>{ </span><span> </span><span style="color:#b48ead;">return </span><span style="color:#d08770;">0</span><span>, </span><span style="color:#bf616a;">errors</span><span>.</span><span style="color:#bf616a;">New</span><span>(&quot;</span><span style="color:#a3be8c;">Cannot find the square root of a negative number</span><span>&quot;) </span><span> } </span><span> </span><span> </span><span style="color:#a7adba;">// choose x to be in the middle of high and low </span><span> </span><span style="color:#bf616a;">x </span><span>:= (</span><span style="color:#bf616a;">high </span><span>+ </span><span style="color:#bf616a;">low</span><span>) / </span><span style="color:#d08770;">2 </span><span> </span><span style="color:#bf616a;">xs </span><span>:= </span><span style="color:#bf616a;">x </span><span>* </span><span style="color:#bf616a;">x </span><span style="color:#a7adba;">// x squared </span><span> </span><span> </span><span style="color:#a7adba;">// keep looping while x squared is not the nearest rounded </span><span> </span><span style="color:#a7adba;">// down integer of any squared integer </span><span> </span><span style="color:#b48ead;">for </span><span>!(</span><span style="color:#bf616a;">xs </span><span>&lt;= </span><span style="color:#bf616a;">v </span><span>&amp;&amp; (</span><span style="color:#bf616a;">x</span><span>+</span><span style="color:#d08770;">1</span><span>)*(</span><span style="color:#bf616a;">x</span><span>+</span><span style="color:#d08770;">1</span><span>) &gt; </span><span style="color:#bf616a;">v</span><span>) { </span><span> </span><span style="color:#b48ead;">if </span><span style="color:#bf616a;">xs </span><span>&gt; </span><span style="color:#bf616a;">v </span><span>{ </span><span> </span><span style="color:#a7adba;">// if x squared is bigger than v, set the value of high </span><span> </span><span style="color:#a7adba;">// to the middle of high and low, which is where x is now </span><span> </span><span style="color:#bf616a;">high </span><span>= </span><span style="color:#bf616a;">x </span><span> } </span><span style="color:#b48ead;">else if </span><span style="color:#bf616a;">xs </span><span>&lt; </span><span style="color:#bf616a;">v </span><span>{ </span><span> </span><span style="color:#a7adba;">// if x squared is smaller than v, set the value of low to x </span><span> </span><span style="color:#bf616a;">low </span><span>= </span><span style="color:#bf616a;">x </span><span> } </span><span> </span><span style="color:#a7adba;">// set x to the middle of high and low </span><span> </span><span style="color:#bf616a;">x </span><span>= (</span><span style="color:#bf616a;">high </span><span>+ </span><span style="color:#bf616a;">low</span><span>) / </span><span style="color:#d08770;">2 </span><span> </span><span style="color:#a7adba;">// calculate and store x squared to save some cycles </span><span> </span><span style="color:#bf616a;">xs </span><span>= </span><span style="color:#bf616a;">x </span><span>* </span><span style="color:#bf616a;">x </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">return </span><span style="color:#bf616a;">x</span><span>, </span><span style="color:#d08770;">nil </span><span>} </span></code></pre> <h2 id="follow-up-question">Follow-up question</h2> <p>How would you adapt your integer square root algorithm from the problem above to return the floating point square root of a given number, instead of an integer?</p> Aquileo | Minimum Palindrome Partitions2013-12-23T00:00:00+00:002013-12-23T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/posts/minimum-palindrome-partitions/<h2 id="problem">Problem</h2> <blockquote>Given a string s, partition s such that every substring of the partition is a palindrome. <p>Return the minimum cuts needed for a palindrome partitioning of s.</p> <p>For example, given s = “aab”, return 1 since the palindrome partitioning [“aa”,“b”] could be produced using 1 cut.</blockquote></p> <h2 id="analysis">Analysis</h2> <p>The first thing we need to do is write a function that is able to determine when a string is a palindrome. A palindrome is a string that reads the same from left to right as from right to left. Therefore, a simple way to test whether a string is a palindrome is to loop over every character in the string and compare it to the string at the same position from the other end. For example, if we have the string &ldquo;madam&rdquo;, we start by looking at the first character, <code>m</code>, and comparing it to the last character, which is also <code>m</code>. Since they are equal, we continue and look at the second character, <code>a</code>, and compare it to the second-to-last character, in this case also <code>m</code>. We continue in this fashion either until we find a pair of characters that is not equal, in which case we return <code>false</code> (not a palindrome), or when we reach the middle of the string, in which case we return <code>true</code>. Here is an implementation of this algorithm in Go:</p> <pre><code>func IsPalindrome(s string) bool { // convert to rune to account for non-ascii strings r := []rune(s) ln := len(r) for i := 0; i &lt; ln/2; i++ { if r[i] != r[ln-i-1] { return false } } return true } </code></pre> <p>Now that we have a function for determining whether a string is palindromic, we can get to the crux of the problem. We desire an algorithm that is able to find the minimum number of cuts we need to make to a string to turn all its parts into palindromes.</p> <p>First, let us consider the brute force solution. This would be to attempt every possible partitioning, and then storing the number of cuts made if it is smaller than any previous number we have found and all the partitions are palindromes. There are N-1 positions where we can cut the string, and we need to consider every combination of these positions.</p> <p>For the string <code>abcd</code>, for example, we can cut it in 3 different positions at once: <code>a|b|c|d</code>. We can also cut two at a time: <code>a|b|cd</code>, <code>a|bc|d</code>, <code>ab|c|d</code>, or one at a time: <code>a|bcd</code>, <code>ab|cd</code>, <code>abc|d</code>, or no cuts at all, for a total of 8 combinations. In general, it is possible to prove that there are <code>2^N</code> possible partitionings for a string of length N. So the complexity of the brute force solution will be O(N.2^N), because we will need to check every partitioning (2^N combinations), and for each partitioning whether every partition is a palindrome (checking N characters in total, worst case). O(N.2^N) is not very good, and we can do better. Let&rsquo;s try to find a more efficient solution.</p> <p>As a rule of thumb, whenever a problem requires us to find the &ldquo;minimum&rdquo; or &ldquo;maximum&rdquo;, a dynamic programming solution should be considered early on. Let us see if we can formulate this problem in a way that leans itself to a DP solution. We start by looking at a section of the full string, from index <code>i</code> to <code>j</code>. If the string from i to j is palindromic, we don&rsquo;t need any more cuts, and the answer is 0. Otherwise we need at least one cut, and the total minimum number of cuts needed to make this section palindromic will be one plus the minimum number of cuts needed to make the two new subsections palindromic. In mathematical notation, it looks like this:</p> <pre><code class="tex">f(i,j) = \begin{cases} min[f(i,k)+f(k+1,j) + 1 \mbox{ for } i \leq k \lt j]&amp; \mbox{if }str[i:j] \mbox{ is not a palindrome}\\ 0&amp; \mbox{otherwise} \end{cases} </code></pre> <p>You will notice that this definition is recursive, which is why it is a dynamic programming solution. We break the string up into smaller subsections, and apply the same algorithm on those substrings. If we implement this as a recursive solution however, the time complexity will be polynomial on the length of the string, which will be unacceptably slow for longer strings. We can instead build a NxN matrix of values calculated up to that point, and solve the problem in O(N^2) time and O(N^2) space. The matrix will be filled from the diagonal outwards, so that the final solution will reside in the corner of the matrix.</p> <p>For example, if we have the string &ldquo;abbaa&rdquo;, the matrix will look like this:</p> <pre><code>[0 1 1 0 1] [0 0 0 1 1] [0 0 0 1 0] [0 0 0 0 1] [0 0 0 0 0] </code></pre> <p>The zeroes on the diagonal represent the fact that the string from index <code>i</code> to <code>i+1</code> will always be palindromic, because it&rsquo;s only one character long. At index 0, 4 we have a value of zero again, because <code>s[0:4]</code> (“abba”) is palindromic. The minimum number of cuts is therefore 1, at index 4.</p> <h2 id="solution">Solution</h2> <p>The following solution is implemented in Go:</p> <pre data-lang="go" style="background-color:#eff1f5;color:#4f5b66;" class="language-go "><code class="language-go" data-lang="go"><span style="color:#b48ead;">func </span><span style="color:#8fa1b3;">MinPalindromeCuts</span><span>(</span><span style="color:#bf616a;">s </span><span style="color:#b48ead;">string</span><span>) </span><span style="color:#b48ead;">int </span><span>{ </span><span> </span><span style="color:#a7adba;">// convert to rune to account for non-ascii strings </span><span> </span><span style="color:#bf616a;">r </span><span>:= []</span><span style="color:#b48ead;">rune</span><span>(</span><span style="color:#bf616a;">s</span><span>) </span><span> </span><span style="color:#bf616a;">l </span><span>:= </span><span style="color:#96b5b4;">len</span><span>(</span><span style="color:#bf616a;">r</span><span>) </span><span> </span><span> </span><span style="color:#bf616a;">table </span><span>:= </span><span style="color:#96b5b4;">make</span><span>([][]</span><span style="color:#b48ead;">int</span><span>, </span><span style="color:#bf616a;">l</span><span>) </span><span> </span><span style="color:#b48ead;">for </span><span style="color:#bf616a;">i </span><span>:= </span><span style="color:#b48ead;">range </span><span style="color:#bf616a;">table </span><span>{ </span><span> </span><span style="color:#bf616a;">table</span><span>[</span><span style="color:#bf616a;">i</span><span>] = </span><span style="color:#96b5b4;">make</span><span>([]</span><span style="color:#b48ead;">int</span><span>, </span><span style="color:#bf616a;">l</span><span>) </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">for </span><span style="color:#bf616a;">u </span><span>:= </span><span style="color:#d08770;">1</span><span>; </span><span style="color:#bf616a;">u </span><span>&lt;= </span><span style="color:#bf616a;">l</span><span>; </span><span style="color:#bf616a;">u</span><span>++ { </span><span> </span><span style="color:#b48ead;">for </span><span style="color:#bf616a;">y </span><span>:= </span><span style="color:#bf616a;">l </span><span>- </span><span style="color:#bf616a;">u</span><span>; </span><span style="color:#bf616a;">y </span><span>&gt;= </span><span style="color:#d08770;">0</span><span>; </span><span style="color:#bf616a;">y</span><span>-- { </span><span> </span><span style="color:#bf616a;">i </span><span>:= </span><span style="color:#bf616a;">l </span><span>- </span><span style="color:#bf616a;">u </span><span>- </span><span style="color:#bf616a;">y </span><span> </span><span style="color:#bf616a;">j </span><span>:= </span><span style="color:#bf616a;">i </span><span>+ </span><span style="color:#bf616a;">u </span><span>- </span><span style="color:#d08770;">1 </span><span> </span><span style="color:#b48ead;">if </span><span style="color:#bf616a;">IsPalindrome</span><span>(</span><span style="color:#bf616a;">string</span><span>(</span><span style="color:#bf616a;">r</span><span>[</span><span style="color:#bf616a;">i </span><span>: </span><span style="color:#bf616a;">j</span><span>+</span><span style="color:#d08770;">1</span><span>])) { </span><span> </span><span style="color:#bf616a;">table</span><span>[</span><span style="color:#bf616a;">i</span><span>][</span><span style="color:#bf616a;">j</span><span>] = </span><span style="color:#d08770;">0 </span><span> } </span><span style="color:#b48ead;">else </span><span>{ </span><span> </span><span style="color:#bf616a;">minCuts </span><span>:= </span><span style="color:#d08770;">1 </span><span>&lt;&lt; </span><span style="color:#d08770;">7 </span><span>- </span><span style="color:#d08770;">1 </span><span> </span><span style="color:#b48ead;">for </span><span style="color:#bf616a;">k </span><span>:= </span><span style="color:#bf616a;">i</span><span>; </span><span style="color:#bf616a;">k </span><span>&lt; </span><span style="color:#bf616a;">j</span><span>; </span><span style="color:#bf616a;">k</span><span>++ { </span><span> </span><span> </span><span style="color:#bf616a;">m </span><span>:= </span><span style="color:#bf616a;">table</span><span>[</span><span style="color:#bf616a;">i</span><span>][</span><span style="color:#bf616a;">k</span><span>] + </span><span style="color:#bf616a;">table</span><span>[</span><span style="color:#bf616a;">k</span><span>+</span><span style="color:#d08770;">1</span><span>][</span><span style="color:#bf616a;">j</span><span>] + </span><span style="color:#d08770;">1 </span><span> </span><span style="color:#b48ead;">if </span><span style="color:#bf616a;">m </span><span>&lt; </span><span style="color:#bf616a;">minCuts </span><span>{ </span><span> </span><span style="color:#bf616a;">minCuts </span><span>= </span><span style="color:#bf616a;">m </span><span> } </span><span> } </span><span> </span><span style="color:#bf616a;">table</span><span>[</span><span style="color:#bf616a;">i</span><span>][</span><span style="color:#bf616a;">j</span><span>] = </span><span style="color:#bf616a;">minCuts </span><span> } </span><span> } </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">return </span><span style="color:#bf616a;">table</span><span>[</span><span style="color:#d08770;">0</span><span>][</span><span style="color:#bf616a;">l</span><span>-</span><span style="color:#d08770;">1</span><span>] </span><span>} </span></code></pre> <h2 id="follow-up-questions">Follow-up questions</h2> <blockquote> What is the time complexity of the solution? </blockquote> <p>The solution runs in O(N^3) time: there are two loops present in the main function, and they run the <code>IsPalindrome</code> function, which is itself O(N). Therefore, the solution is O(N^3).</p> <blockquote> How would you improve upon the solution above? (Hint: the solution can be improved both in terms of efficiency and in terms of memory usage) </blockquote> <p>One quick optimization we can make is to check, at the start of the function, whether the full string is already a palindrome. If it is, we can immediately return 0 for a total cost of O(N), the cost of checking that the string is a palindrome.</p> <p>We can improve the solution further by noting that a substring <code>str[a:b]</code> is only a palindrome if the characters at <code>a</code> and <code>b</code> are equal, and <code>str[a+1:b-1]</code> is also a palindrome. We may therefore build an NxN table to store whether every substring of <code>str</code> is a palindrome or not. We could do this during preprocessing at a cost of O(N^2), and then use this table during our main function execution to do lookups in O(1), which brings the total time complexity down to O(N^2).</p> <p>If memory constraints are tight, we may also improve on the solution above by using a one-dimensional array instead of a 2D array. This removes some of the memory overhead of storing separate arrays.</p> Aquileo | I Rewrote my Blog in Go2013-08-14T00:00:00+00:002013-08-14T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/posts/i-rewrote-my-blog-in-go/<p><em>Wow, this hit <a href="https://news.ycombinator.com/item?id=6211250">Hacker News front page</a>! Thanks for all the encouraging feedback; I added some thoughts and clarifications to the <a href="#afterthoughts">bottom of the post</a> for those who asked.</em></p> <h4>The short version</h4> <p><img src="/images/ironzebra-switch-to-go.png" alt="Page load time decreased by a factor of almost 100" title="Page load time decreased by a factor of almost 100"/> </p> <h3>The full story</h3> <p>As part of my recent interest in learning Golang, I decided it would be a good idea to rewrite the IronZebra blog that, funnily enough, was itself the result of my venture to get acquainted with Django three years ago. The Revel framework looked like a good fit for this, and I decided to give it a go.</p> <p>Overall, the experience was great. I had become familiar with the basics of Go before through writing a bunch of <a href="https://web.archive.org/web/20160820021716/https://github.com/hermanschaaf/go-euler">solutions to Euler problems</a>, and this provided a good enough knowledge baseline for building a site in Revel.</p> <p>I skimmed through the Revel docs to see how things like URL-routing work, and decided to plug <a href="https://labix.org/mgo">mgo</a> into it as an interface to my Mongo database. mgo is an excellent driver for MongoDB, and working with it was a breeze except for one or two surprises where the syntax deviated from the normal MongoDB structure. For one, the <code>FindOne</code> method was dropped for a chained combination of <code>Find</code> and <code>One</code>:</p> <pre><code>collection.Find(bson.M{&quot;title&quot;: title}).One(&amp;result) </code></pre> <p>Regarding Revel: Revel&rsquo;s code reload is nice, and I like the way it simplified URL routing by allowing you to edit a well-structured text file which then gets compiled into Go code before serving the site. It&rsquo;s a good solution to a problem that might have looked much more ugly had it been done only in Go. The one downside to Revel at the moment (although this is on the <a href="https://web.archive.org/web/20160820021716/http://robfig.github.io/revel/#wishlist">wishlist</a>) is that it only ships with the default Go template language. Documentation on this isn&rsquo;t all that good, and I felt like it doesn&rsquo;t solve website templates nearly as well as Jinja2 or Django templates. The template inheritance, for example, seems clunky and I ended up with files containing only closing html tags &ndash; ugly. Maybe I should have read the documentation more closely, but I wanted to iterate fast and get things done. Plugging in your own template system is surely also possible, but for a first-time exploratory project this prospect seemed like a bit much.</p> <p>I spent most of the time creating the admin functionality for editing and creating new posts. The admin section allows for markdown input (using <a href="https://github.com/russross/blackfriday">blackfriday</a>) and logging in with a bcrypt-encrypted password that gets stored in a user collection in Mongo. To give you an idea, here&rsquo;s what the admin editor looks like:</p> <p><img src="/images/ironzebra-admin-section.png" alt="The IronZebra blog Golang admin section" title="The IronZebra blog Golang admin section"/> </p> <h4>The results</h4> <p>The speed improvement was staggering.</p> <p><img src="/images/ironzebra-switch-to-go.png" alt="Page load time decreased by a factor of almost 100" title="Page load time decreased by a factor of almost 100"/> </p> <p>As differences between the two versions go: Both for the old Django site and for this Revel-based Go site, I never did anything along the lines of caching - no static page generation, no memcache, no <a href="https://github.com/golang/groupcache">groupcache</a> - you can call it laziness, but either way, it makes for a more fair comparison between the two now. Secondly, I am now loading less static assets. I removed the Disqus comments and the many many lines of CSS from the old site and replaced it with only a couple of lines of CSS alongside a CDN-hosted copy of Twitter Bootstrap. Finally, the Go site is deployed to a free instance of Heroku and the MongoDB hosted on a developer version of Mongolab, while the old Django site was hosted on a Webfaction shared server. All these things influence the validity of a direct speed comparison between the two versions, but the speed improvement is nevertheless too overwhelming to attribute only to these small changes. And in fact, many of these changes might even have negatively impacted the speed of the new version in exchange for saving on the monthly bills.</p> <p>But the end result: <em>pages that load up to a 100 times faster on average</em>, according to Google&rsquo;s Webmaster Tools. Search traffic has also been steadily increasing this last month, possibly influenced by Google giving higher weight to sites that load really fast.</p> <p>If you are interested in using this yourself or contributing, or even collaborating on making this into an open blog-engine written in Go - send me a <a href="https://github.com/hermanschaaf/ironzebra">ping on Github</a> - this project is open-source and on there in its entirety. Enjoy!</p> <p><em>Edit on 15 August 2013:</em></p> <h4>Afterthoughts <a id="afterthoughts"></a></h4> <p>This hit Hacker News front page yesterday and it seems like this humble post started somewhat of a flame war in the comments section. I decided not to jump in, especially since I couldn&rsquo;t really provide experimental answers to their very valid questions while 500 visitors were hitting the site every minute. Regardless, I also received so much encouraging feedback, emails and tweets; thanks all!</p> <p>I hope to offer some small clarifications:</p> <ul> <li><p>A lot of folks on HN commented on the Disqus removal. I&rsquo;m afraid the graph was only for the time Googlebot took on average <em>to download the HTML</em>. I mentioned the removal of Disqus in the post, but that wouldn&rsquo;t have a significant impact on this graph&rsquo;s page load time, as others also later pointed out in the comments.</p></li> <li><p>Yes, the old site was incredibly slow. Anything would be better. That&rsquo;s part of the reason I rewrote it. I never spent any time making it efficient, which is why I didn&rsquo;t even have caching. It was also hosted on Webfaction, which is, in my eyes, the most likely source for the slowness. If your Webfaction app is not in memory right now, it seems to take an incredibly long time to load it back in. User Jgrubb also wisely pointed out <a href="https://news.ycombinator.com/item?id=6213362">how much of a difference switching servers can make</a>. He&rsquo;s right, those graphs look mightily similar!</p></li> <li><p>All this said, I think a lot of folks missed the point of this post, if there was any point at all :) I didn&rsquo;t mean to imply &ldquo;Go, and nothing but Go, made my blog 100x faster&rdquo;. I meant to just say: &ldquo;I rewrote this blog in Go, streamlined a bunch of other things, and now it&rsquo;s 100x faster, yeay.&rdquo; I realize that the graph was suggestive of the first one, my apologies for that.</p></li> <li><p>There are good reasons for writing a blog from scratch and not using some existing solution for serving static files. One, learning. Two, fun. Three, a blog engine of this kind is like the &ldquo;Hello World&rdquo; of web applications. It covers a lot of the common things you&rsquo;ll normally need to do in a bigger, product-oriented web application. Basic GET and POST, user login and database connections. Proving that it&rsquo;s easy to write a CRUD blog like this is an early exploratory exercise in determining whether it would be easy to write larger-scale applications that scale.</p></li> <li><p>And hey, after all is said and done, the Go-based IronZebra blog survived being posted on the front page of Hacker News on a free Heroku instance (pretty well at that, never even taking long to load), all without caching. If this were still Django, and still hosted on Webfaction, I don&rsquo;t believe it would have been the case. That&rsquo;s all I&rsquo;m saying :)</p></li> <li><p>I also added some more details in a <a href="http://www.slideshare.net/hermanschaaf/i-rewrote-my-blog-in-go-25274956">presentation on slideshare</a> - I&rsquo;ll be presenting this <a href="http://gengo.doorkeeper.jp/events/5386">in Tokyo tomorrow</a>, drop by if you can!</p></li> </ul> Aquileo | Lessons Learnt from Startup Weekend2011-12-05T00:00:00+00:002011-12-05T00:00:00+00:00 Herman Schaaf https://hermanschaaf.com/posts/lessons-learnt-from-startup-weekend/<p>This past weekend, I attended the Startup Weekend event in Hsinchu, Taiwan. It was a great event, well-organized and a lot of fun. The premise of Startup Weekend is simple: On the Friday night, anyone can pitch their idea in 60 seconds. The participants then vote on their favorite ideas, of which 10 are chosen, and everyone then divides themselves into groups for these ideas. Then you have the Saturday and Sunday to create a first-iteration business: Determine the Idea and Target Market, do Surveys for validation, Design, Build, get feedback, iterate, and try and have a working business by the Sunday evening, where you have 5 minutes to pitch your achievements to a panel of judges.</p> <p>My team was named <a href="https://web.archive.org/web/20160616201414/http://www.takemyroute.com/">TakeMyRoute</a>, a tool to help you plan your travels or daytrips, share it with friends, and have others gain advantage from your research and give their own experiences while traveling the route. On Friday night we only had a basic idea of what the site needs to do, but by Saturday afternoon we had determined a marketing strategy and monetization strategy, done surveys, designed the pages for a Version 1.0, and started coding. My responsibility was to design and code the front-end of the site within 24 hours. Tall task, but by Sunday afternoon we had a live site up and running, and ended up getting 2nd place in the competition.</p> <p>Here are some lessons I took from the experience that I hope can be useful to future participants of Startup Weekends across the globe, specifically those who are responsible for building the prototype.</p> <h3 id="lesson-1-it-s-a-demo-not-version-1-0">Lesson 1: It’s a demo, not Version 1.0</h3> <p>The judges don&rsquo;t look at your code, but they definitely like seeing a working demo. In fact, the teams that placed first, second and third all had basic demos of the concept up and running, and either demoed it live, or showed a video of it in use. So it&rsquo;s clearly important to really have code working, and not just some designs that show the idea.</p> <p>However, you only have 24 hours. In all probability, what you build in that 24 hours will have to be rewritten if you choose to go on with the project after Startup Weekend, no matter what you do. So dont&rsquo; worry about making the code understandable. Don&rsquo;t worry about maintainability. Don&rsquo;t worry about standards compliance. Don&rsquo;t worry about efficiency. Don&rsquo;t worry about browser bugs. If it works in just one browser, fine, you&rsquo;ll just show that one browser. As long as one browser understands what you wrote and displays it correctly, you&rsquo;re fine. Realizing this early on can save a lot of time. Our demo, for example, was just three files, each with around 500 lines of code, javascript, css and html all mixed together. But it works fine, and illustrates the concept well.</p> <h3 id="lesson-2-design-is-just-as-important-as-code-if-not-more">Lesson 2: Design is just as important as code, if not more</h3> <p>Make sure you have good (really good) designs for the prototype, and if you have time, add some nice visual effects to the demo. It gives the illusion of a well-rounded, working project. In the end, that&rsquo;s what people are going to fixate on, and they&rsquo;ll be quite forgiving if one part of the code is a bit buggy or not implemented yet. You can even make sure they just don&rsquo;t see those parts, but are impressed by the quality and attractiveness of the designs that are already built into your product.</p> <h3 id="lesson-3-use-api-s-and-existing-code-where-possible">Lesson 3: Use API’s and existing code where possible</h3> <p>This is mostly about saving lots of time. Before you start implementing something, first google if there exists an API or plugin that does exactly what you want. If there does, it can save you hours (or days) of development. We used Panaramio&rsquo;s API for images, Google Places for populating the map with suggestions, and of course Google Maps for the actual map and Directions for the route planning. This gave us a working, extremely impressive prototype in a matter of hours. Another thing that helps is to use some code snippets available online, or even better, some of your own that you wrote for other projects. It saves you time thinking about how to implement something if you can just copy-paste.</p> <h3 id="lesson-4-delegate">Lesson 4: Delegate</h3> <p>You have all the skills to build the whole project? Great! But you can&rsquo;t. Choose the part you&rsquo;re the best at or that&rsquo;s the most important to finish for the Demo (for me, that&rsquo;s front-end), and give other parts to team-members who are even just partly skilled in that area. You can&rsquo;t do it all yourself. And even if your team-mate is not an expert, he has 24 hours to figure things out as necessary. He&rsquo;ll still achieve more than you would have, since you&rsquo;ll have no time at all to spend on that.</p> <p>As for anything you come across that you realize you&rsquo;ll now need to spend time on, but someone else could have done it (maybe changing an image from .jpg to .png, for example), delegate. Ask someone to do it and send it to you, while you carry on coding.</p> <h3 id="lesson-5-good-lighting">Lesson 5: Good Lighting</h3> <p>This is more of a practical lesson that I personally learnt: Good lighting in the room you are working is crucial. We had some really strong lights in our room, and that (along with ample caffeine) caused us to not become tired so quickly, my eyes to not feel as tired as they usually do and able to achieve a great amount in just a day.</p> <h3 id="lesson-6-have-fun-and-share-your-progress-constantly">Lesson 6: Have fun, and share your progress constantly</h3> <p>Just got one basic feature working? Be glad about it, and tell your team to come have a look as you demo it. This boosts team spirit greatly, gives you some quick feedback on the direction you&rsquo;re heading in, and makes sure everyone stays motivated. It&rsquo;s always really encouraging to see your team members achieving things - it inspires you to also have something great to show off. By the end of Startup Weekend, our team spirit was really high, and I think that&rsquo;s due in a large part to our focus and constant achievements, right from the beginning.</p> <h3>Conclusion</h3> <p>Overall, Startup Weekend was a great experience, and I&rsquo;ll definitely recommend it to anyone. If you&rsquo;re a web developer / designer / app developer, your presence will be greatly appreciated, you&rsquo;ll learn a lot of new things, and meet a lot of cool people. And if you&rsquo;re lucky, you might just ignite a business that ends up growing to global scale.</p>