Tuesday, May 26, 2020

Riddler Classic, May 23, 2020—Holy Mackerel!

Another one using Peter Norvig's word list.

It turns out that the word "mackerel" has a curious property: there is exactly one of the fifty states that it has no letters in common with. So we will call a word with that property "a mackerel". There are quite a few of them. (It doesn't matter which state name is the unique one disjoint from the word. The state for "mackerel" is Ohio, but for "goldfish" it's Kentucky.)

For purposes of this problem, the permissible words are those in Peter Norvig's word list.

So... the Riddler poses the following questions:
  • Which mackerel (or mackerels) are the longest?
  • For extra credit, which state has the most mackerels?
I'd pose an extra question: are there any mackerel-less states?

The last Riddler using that list took some work to make efficient, so I was a bit worried... but I didn't need to be.

I've been doing more C than Python lately, so after some Googling to remind myself of some things, I ended up with this:


class DefaultEmpty(dict):
    def __missing__(self, key):
        self[key] = rv = []
        return rv

def setDict(wordFile):
    with open(wordFile, "r") as f:
        return {w:set(w) for w in f.read().splitlines()}

def isMackerel(wordSet, stateDict):
    nonOverlapper = None
    for state, stateSet in stateDict.items():
        if stateSet.isdisjoint(wordSet):
        if nonOverlapper:
            return None
        else:
            nonOverlapper = state
    return nonOverlapper

def mackerels(stateFile, wordFile):
    stateDict = setDict(stateFile)
    wordDict = setDict(wordFile)
    mackerelDict = {}
    stateMackerels = DefaultEmpty()
    for word, wordSet in wordDict.items():
        state = isMackerel(wordSet, stateDict)
        if state:
            mackerelDict[word] = state
            stateMackerels[state].append(word)
    return mackerelDict, stateMackerels

First we have the usual dictionary with a default value, in this case the empty list.

Next, a function that takes a file and generates a map from lines of the file to the sets created from them. (splitlines() pulls the newline characters, thank goodness! Otherwise we'd see no mackerels at all.)

Next, isMackerel(), which, given the set made from a word and the state dictionary, will return the appropriate state if the word is a mackerel and return None if the word isn't a mackerel.

Finally, what we really want: something that takes a file of state names (we were lazy and created the file with all lower case and spaces mashed out, e.g. "westvirginia") and the word file and creates two dictionaries: mackerelDict, which maps mackerels to their states, and stateMackerels, which maps states to their lists of mackerels--note that any state with no mackerels won't be a key of stateMackerels.

With all that, all we need to do is...

from mackerel import mackerels
mackerelDict, stateMackerels = mackerels("states.txt", "word_list.txt")
mackerelsByLength = sorted(mackerelDict.keys(), key=len, reverse=True)
statesByMackerelCount = sorted(stateMackerels.keys(), key=lambda s: len(stateMackerels[s]), reverse=True)

and we have what we need to answer the questions.

>>> [len(s) for s in mackerelsByLength[0:5]]
[23, 23, 21, 21, 20]
>>> mackerelsByLength[0:2]
['counterproductivenesses', 'hydrochlorofluorocarbon']

So, there are two 23-character mackerels, and they're the longest using this word list.

>>> statesByMackerelCount[0]
'ohio'
>>> len(stateMackerels["ohio"])
11342


Ohio has the most mackerels, 11,342 of them.

>>> len(stateMackerels.keys())
32


Eighteen states have no mackerels at all! Shame on us; we should've returned stateDict.keys() so we could easily get the names of the mackerel-less states. We do know the ones with mackerels:

>>> stateMackerels.keys()
['wyoming', 'colorado', 'alaska', 'tennessee', 'iowa', 'nevada', 'maine', 'mississippi', 'newjersey', 'oklahoma', 'delaware', 'illinois', 'indiana', 'maryland', 'texas', 'wisconsin', 'newyork', 'michigan', 'kansas', 'utah', 'virginia', 'oregon', 'connecticut', 'montana', 'newmexico', 'vermont', 'hawaii', 'kentucky', 'northdakota', 'missouri', 'ohio', 'alabama']

How long did all this take to run? The slowest part was running mackerels(), which took a bit over three seconds on my AMD FX-8370; nothing else seemed to take human-perceptible time.

Monday, February 10, 2020

Riddler Classic, Feb 7, 2020

Sometimes I can figure out the solution to either the Riddler Express or Riddler Classic. This week is the first time I'm reasonably confident I have them both.

It helped that Riddler Express was really, really easy this time. Riddler Classic, though, takes some more thought.

It started with a math professor, James Propp, whose child asked about the vertical bars used in math notation for absolute value. Propp initially said "They're just like parentheses so there isn't any ambiguity" but then realized that there is ambiguity. |-1|-2|-3| could be either

  • |-1| - 2 * |-3| = 1 - 2 * 3 = -5 or
  • |-1 * |-2| -3 = |-1 * 2 - 3| = |-2 - 3| = 5
...but wait! It could also be |-1| * -2 * |-3| = 1 * -2 * -3 = -6, so perhaps there's more ambiguity than the Riddler and Professor Propp realize. What's the source of this additional ambiguity?
  • '-' can be unary minus or it can be subtraction
  • in math notation, multiplication is expressed via concatenation: "xy" is x times y.
*NOTE*: See the update below. Professor Propp pointed out my mistaken interpretation.  I'll leave my erroneous code here--I was happy to have a problem that made me think seriously despite being sick as the proverbial dog, and I'll make a point of remembering the part about iterating over range(2^n).

If we were using programming language notation, an "open vertical bar" would be written "abs(" and a "close vertical bar" would be written ")".  We can generate the valid expressions corresponding to |-1|-2|-3|-4|-5|-6|-7|-8|-9| by generating the validly nested balanced strings of parentheses.


def rawCombinations(n):
    # generate all n-character strings with n / 2 open parens and
    # n / 2 close parens to correspond to vertical bars that surround
    # an expression we're taking the absolute value of. We only really
    # care about balanced strings, so we always start with ( and end
    # with ). For the Riddler problem, n = 10.
    for c in combinations(range(n - 2), (n - 2) // 2):
        yield f"({''.join('(' if i in c else ')' for i in range(n - 2))})"


def isValid(s):
    # return True iff the input string has correctly nested parentheses
    nestLevel = 0
    for c in s:
        nestLevel += 1 if c == '(' else -1
        if nestLevel < 0:
            return False
    return nestLevel == 0

Now, what about the digits? It turns out that what you do depends on the surrounding parentheses:
  • for "((", insert "-n *"
  • for "()", insert "-n"
  • for ")(*, either insert " - n * " or "* -n * "
  • for "))", insert "* -n"
So for a valid string of parentheses, there's some number of occurrences of ")(" in it; call it n.  Then we know there are 2^n possible expressions corresponding to that parenthesis string. I thought about recursion, but then realized that was silly. The numbers from 0 to 2^n - 1 correspond to a set of choices for each ")(", so it's a simple iterator:

def exprs(s):
    numAmbiguous = 0
    for i in range(len(s)-1):
        if s[i:i+2] == ')(':
            numAmbiguous += 1
        
    for i in range(2**numAmbiguous):
        digit = 1
        ambiguousIndex = 0
        result = ''
        for j in range(len(s)-1):
            pair = s[j:j+2]
            if pair[0] == '(':
                result += 'abs('
            elif pair[0] == ')':
                result += ')'
            if pair == ')(':
                if i & (1 << ambiguousIndex):
                    result += f' * {-digit} * '
                else:
                    result += f' - {digit} * '
                ambiguousIndex += 1
            elif pair == '((':
                result += f'{-digit} * '
            elif pair == '))':
                result += f' * {-digit}'
            elif pair == '()':
                result += f'{-digit}'
            digit += 1
        result += ')'
        yield result

Python, like a lot of "scripting" languages, has an eval() function you can pass a string to and get back a value, so all that's left is

def calcValues():
    # get the possible expressions, evaluate them, and build a dictionary
    # mapping value to the list of expressions that evaluate to it. (I'd
    # just keep a count, but I'm curious and having the expressions might
    # be good for testing.)
    c = defaultdict(list)
    for s in filter(isValid, rawCombinations(10)):
        for e in exprs(s):
            c[eval(e)].append(e)
    return c

You'll need

from itertools   import combinations
from collections import defaultdict

to make Python happy.

That done...

Python 3.7.6 (default, Jan  8 2020, 19:59:22)  
[GCC 7.3.0] :: Anaconda custom (64-bit) on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from riddlerClassic020720 import calcValues
>>> foo = calcValues()
>>> len(foo)
103


There's our answer. Just out of idle curiosity...

>>> sorted(list(foo))
[-362880, -362879, -181584, -181296, -175392, -60486, -60485, -60474, -57456, -3
0264, -30263, -30216, -30215, -17064, -17063, -16848, -14112, -13392, -11376, -5112, -5111, -3144, -3143, -3050, -3049, -3038, -2966, -2904, -2903, -2508, -1902, -1901, -1890, -1728, -1727, -924, -923, -918, -917, -906, -468, -467, -288, -287, -234, -233, -140, -139, -128, -56, 6, 7, 114, 124, 142, 162, 216, 390, 450, 460, 498, 726, 762, 763, 774, 862, 1224, 1944, 2446, 2450, 2790, 2904, 2905, 2998, 2999, 3010, 3082, 3144, 3145, 4104, 4968, 4969, 8530, 11376, 11377, 15096, 15106, 15134, 15144, 17064, 28512, 28513, 57456, 60426, 60474, 60475, 60486, 60534, 175392, 181438, 181442, 362880, 362881]

If you're like me, you're seriously tired of all those, as the Riddler elegantly put it, "those annoying memes about order of operations", but I have to admit that when I took a look at

>>> foo[7]
['abs(-1) - 2 * abs(-3) * -4 * abs(-5) - 6 * abs(-7) - 8 * abs(-9)']

I spent a little time convincing myself that yes, that really does evaluate to 7.

UPDATE: as mentioned above, it turns out that there's really only one choice for ')(', namely '- n *'. That simplifies exprs(), which is now just expr() and returns a value rather than yielding a sequence. In passing, we made calcValues() take the number of | as a parameter so we could easily try smaller cases. Here it is...

# Ridddler Classic for 02/07/2020
# How many values can the ambiguous expression
#  |-1|-2|-3|-4|-5|-6|-7|-8|-9|
# evaluate to?

from itertools   import combinations
from collections import defaultdict

def rawCombinations(n):
    # generate all n-character strings with n / 2 open parens and
    # n / 2 close parens to correspond to vertical bars that surround
    # an expression we're taking the absolute value of. We only really
    # care about balanced strings, so we always start with ( and end
    # with ). For the Riddler problem, n = 10.
    for c in combinations(range(n - 2), (n - 2) // 2):
        yield f"({''.join('(' if i in c else ')' for i in range(n - 2))})"

        
def isValid(s):
    # return True iff the input string has correctly nested parentheses
    nestLevel = 0
    for c in s:
        nestLevel += 1 if c == '(' else -1
        if nestLevel < 0:
            return False
    return nestLevel == 0


def expr(s):
    # Convert a balanced, properly nested string of parentheses to an
    # evaluatable expression. Upon reflection, we don't think there's
    # the additional ambiguity we thought existed.
    digit = 1
    result = ''
    for j in range(len(s)-1):
        pair = s[j:j+2]
        if pair[0] == '(':
            result += 'abs('
        elif pair[0] == ')':
            result += ')'
        if pair == ')(':
            result += f' - {digit} * '
        elif pair == '((':
            result += f'{-digit} * '
        elif pair == '))':
            result += f' * {-digit}'
        elif pair == '()':
            result += f'{-digit}'
        digit += 1
    return result + ')'


def calcValues(n):
    # get the possible expressions, evaluate them, and build a dictionary
    # mapping value to the list of expressions that evaluate to it. (I'd
    # just keep a count, but I'm curious and having the expressions might
    # be good for testing.)
    c = defaultdict(list)
    for s in filter(isValid, rawCombinations(n)):
        e = expr(s)
        c[eval(e)].append(e)

    return c

After which...

>>> foo = calcValues(10)
>>> len(foo)
42
>>> sorted(foo)
[-362879, -60485, -60474, -30215, -17063, -5111, -3143, -3049, -3038, -2966, -2904, -1901, -1890, -923, -917, -906, -467, -287, -233, -139, -128, -56, 6, 114, 124, 142, 216, 390, 450, 460, 726, 1944, 2446, 2790, 4968, 8530, 15096, 15106, 28512, 60426, 181438, 362880]
>>> sorted(len(foo[s]) for s in foo)
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
>>>

so that there are really only 42 values, and no two of the expressions evaluate to the same number.

UPDATE(2), of the serious forehead slap flavor: I should've realized sooner that what is now expr() could be expressed much more simply:

fmtStr = {')(':') - {} * ', '((':'abs(-{} * ', '))':') * -{}', '()':'abs(-{}'}

def expr(s):
    # Convert a balanced, properly nested string of parentheses to an
    # evaluatable expression..

    return ''.join(fmtStr[s[i:i+2]].format(i+1) for i in range(len(s)-1)) + ')'

We had to drop back to using str.format(), because f-strings aren't strings. You might think that you can have a dictionary with f-strings as values, but it won't work the way you might think.  Also, at a minor cost in DRYness, we folded the leading character mapping into the format string.

UPDATE(3): I didn't quite fully excise my misconception. Changing fmtStr to

fmtStr = {')(':') - {} * ', 
          '((':'abs(-{} * ',
          '))':') -{}',       # pulled the '*'
          '()':'abs(-{}'}

gives the proper result.

Thursday, June 27, 2019

A flashback and analogy

You've probably heard about how the notion of sum types (e.g. Algol 68 unions, Rust enums, Haskell types) and product types (e.g. tuples or structs) generalizes, e.g. [a] can be thought of as ${a^0} + {a^1} + {a^2} + {a^3} + ...$ where the exponent corresponds to the length of the list.

This morning I had a flashback from that to my days in introductory numerical analysis and Taylor series, Chebyshev polynomials, and other infinite bases for function spaces. Just as evaluating, e.g., exp(x) can be done by evaluating the first N terms of the corresponding Taylor series where N may vary depending on x but is nonetheless finite, in Haskell, as long as you only evaluate finitely many elements of an "infinite" list, you can do what you need to do.

Friday, June 21, 2019

I wish I were a dog

As I've mentioned recently, I'm now in a Meetup for people in the Des Moines, IA area where we're seriously doing the problems and paying close attention to the text, so I noticed this function intended to map your dog's age in years to a corresponding age in people years, while also introducing the beginning Haskell programmer to guards:

dogYrs :: Integer -> Integer
dogYrs x
   | x <= 0    = 0
   | x <= 1    = x * 15
   | x <= 2    = x * 12
   | x <= 4    = x * 8
   | otherwise = x * 6

Looking at it, you can see that yes, dogs mature faster than people but the rate tapers off with increasing age (but still faster than people). OK...but then something looked wrong. A function like this ought to be monotonically increasing, right?

map dogYrs [0..5]
[0, 15, 24, 24, 32, 30]

So dogs don't age in human years between 2 and 3, and get younger in human years between 4 and 5? (If myDog took and returned types in Fractional, things would get even weirder, because then dogs would celebrate their second birthday plus epsilon by getting almost three years younger in human years, and their fourth birthday plus epsilon by getting almost eight years younger in human years.)

As a human, I feel cheated. As a person looking to get better at Haskell, I think I'd better write a quickCheck test that a function is monotonically increasing.

Monday, June 10, 2019

"Assignment" versus parameter passing

It's been a while. Still looking for work, but I'm happy to say that there is now a Des Moines area meetup for people learning Haskell, so that I'm going through Haskell Programming from First Principles more seriously this time and doing all the exercises.

Thanks to that, I realize that one can express the one well-behaved function of type a -> b -> a as curry fst, but I am confused by one problem (so far). It's the first of a set that has the student modify a type signature and see how it affects the binding of a particular expression to the variable whose type signature is changed. Initially we have

i :: Num a => a
i = 1

and the change is to

i :: a
i = 1 

The first one clearly works; Num a => a is the type of numerical constants (without decimal points) in Haskell. The second, if you try it, fails, with ghci nudging you to put back the typeclass constraint... but at this point my time working on compilers comes back to me, and I recall the following: semantically, passing a parameter to a function is assignment. You assign the actual parameter to the corresponding formal parameter (really, you put it where the ABI says the parameter should go for the called function to get to it).

So... if you can't assign 1 to i when i's type signature is a, why can you pass 1 to id? Haskell must treat the two differently. Looks like I just have to find out what the difference is.

Tuesday, January 30, 2018

Trailing zeroes

A week or so ago someone mentioned the following problem:
How many trailing zeroes does 1000! have [when written out in base ten, of course]?
Easy enough to find...

Prelude> length $ takeWhile (== '0') $ reverse (show (product [1..1000])) 
249
Prelude>


But is there a better way? Yes, there is. After reading that someone said he did it in his head, the way to do it in your head occurred to me. *slaps forehead*

1000!, like any positive integer, has a unique prime factorization. In particular, we're interested in the primes 5 and 2, because 10 is 5 * 2. They certainly both appear in the prime factorization of 1000!, and they thus have positive exponents in said factorization. Let m be the exponent for 5, and let n be the exponent for 2. How to calculate them? Let's start with five.
  • There are 200 multiples of 5 between 1 and 1000.
  • There are 40 multiples of 25 between 1 and 1000.
  • There are 8 multiples of 125 between 1 and 1000.
  • There's 1 multiple of 625 between 1 and 1000.
You recognize, of course, that 5, 25, 125, and 625 are positive powers of 5. The multiples of 625 contribute four to m. The multiples of 125 contribute 3... but one of them is 625, so we don't want to count things more than once. Ditto for multiples of 25 and multiples of 5... but there's an easy way to make sure you get the right count: just add 200, 40, 8, and 1. That counts multiples of 25 twice, as they should be, multiples of 125 three times, and multiples of 625 four times. The sum: 249. Looks familiar.

So, how about 2?
  • There are 500 multiples of 2 between 1 and 1000...
Hey, wait a minute. That's already way bigger than 249, so the minimum of m and n is exactly 249, and that's our answer, because it takes a five and a two to have a ten in the product, and there are exactly as many trailing zeroes as there are powers of 10 in 1000!

Wednesday, September 27, 2017

Sum of multiples problem

One of the exercism.io problems generalizes a Project Euler problem: what's the sum of multiples of 3 and 5 less than 1000? Seems simple enough:
sum [3, 6..999] + sum [5, 10..999]
but that's too big; it counts some values twice. With a little thought, though, you'll realize that the duplicates are the multiples of 15, so
sum [3, 6..999] + sum [5, 10..999] - sum[15, 30..999]
will do the trick. Why 15? Well, it's 3 * 5... but more on that later.

The exercism.io problem makes the upper bound a parameter, and allows an array (or list, depending on the language) of factors. Looking around (which you can do on the site after you've submitted your own solution), one sees two typical approaches:
  • Go through [1..limit - 1], pick out the values that are multiples of one or more of the factors, and add up the result.
  • Create a set or hash table from [[f, 2*f..limit - 1] f <- factors] (thus getting rid of the duplicates), and add up the elements of the set/keys of the hash table.
Both work, but they have drawbacks:
  • Divisibility checking means using the relatively expensive mod, potentially repeatedly, for each value in [1..limit - 1], and as many times as possible for values that aren't summed. As limit grows, that overhead grows.
  • Sets and hash tables build up an underlying data structure that grows with each value added to it. As limit grows, so does RAM usage. (My Python version using this method, handed an upper bound much larger than the exercism.io test values and set up to time with Python's timeit package, quickly sucked down immense amounts of virtual memory and had to be killed.)
There ought to be a way to generalize what we did for the initial [3, 5] case... and indeed there is. What did we do? We took the sum of the multiples of each factor and then subtracted off the sum of the duplicates. If there's just one factor, that sum is zero. If there are two, it's the sum of the multiples of... the product of the factors? Not quite!

Suppose that our factors are 6 and 21. 6 * 21 is 126, but 7 * 6 == 2 * 21 == 42. We want not the product, but the least common multiple of the two factors. 3 and 5 are relatively prime, so their least common multiple is their product (lcm m n == m * n `div` (gcd m n), and m and n are relatively prime iff their greatest common divisor is 1.) Try to fake us out, eh?

More generally, if you have factors f1, f2, ..., fn, each fi gives rise to {lcm fi fj | i < j}  as new sets of factors whose multiples are duplicates of the multiples of fi--but those in turn have duplicate issues, so it's time to recur. You won't recur forever, because the sets of factors get smaller each time.

One more thing... we don't need to evaluate sum [f, f+f, .. limit - 1] at all. There's a better way to do it, and since Young Sheldon had its premiere this week, let's return to the 19th century and Young Carl to learn why we don't have to generate that arithmetic sequence or count on GHC to do fusion. Young Carl Friedrich Gauss was assigned the problem of adding up all the integers from one to one hundred (in the first version I read, the class was assigned the problem, and only after solving it could a student go out to recess). The teacher looked, but Carl wasn't busily adding numbers. Soon he wrote down a number and handed the paper in, and to the teacher's amazement it was correct. The math book I learned it from explained Gauss's insight this way: consider the numbers from 1 to 100 written down twice: once in ascending order, and immediately under that in descending order, and consider corresponding terms in the two sequences. The first, 1 and 100, add up to 101. To get from one pair to the next, the top increases by 1 and the bottom decreases by one, so the sum remains the same: 101. There are 100 such pairs, so collectively they add up to 100 * 101 = 10100... but remember, we wrote the sequence down twice, so that sum is twice the value we want. The number young Carl wrote was 5050, and in general, the sum of the integers from 1 to n is n * (n + 1) / 2, and as an immediate consequence the sum of the multiples of f less than limit is f * n * (n + 1) `div` 2 where n = (limit - 1) `div` f.

Here's what I ended up with. Haskell has both gcd and lcm functions, I'm happy to say.

sumOfMultiples :: [Integer] -> Integer -> Integer
sumOfMultiples factors limit =
    sum (map oneFactorSum factors) -
        sum (map (`sumOfMultiples` limit) (dupFactors factors))
    where sumOneTo     n  = n * (n + 1) `div` 2
          oneFactorSum f  = f * sumOneTo ((limit - 1) `div` f)
          dupFactors   fs = map lcmHead $ take (length fs - 1) (iterate tail fs)
          lcmHead      fs = [lcm (head fs) f | f <- tail fs]

Sunday, August 06, 2017

a longest path problem

fivethirtyeight.com has a weekly column, "The Riddler". The Riddler poses two problems in each column. The first, "Riddler Express", is intended to be an easier problem that one can solve quickly, while the second, "Riddler Classic", is more difficult. Modulo vacations, it appears each Friday, and if you submit a solution by the end of the following Sunday (Eastern Time) , you will be among those who might be given credit for the solution in the next column.

The August 28th column's Classic problem is as follows: what's the longest sequence of integers x[i] for 1 ≤  i ≤  n such that
  • for all i, 1 ≤ x[i] ≤ 100
  • for all i < n, either x[i+1] is a multiple of x[i] or x[i+1] is a factor of x[i]
  • for all i, j, x[i] = x[j] iff i = j, i.e. the x[i] are all distinct
(The last constraint avoids trivial sequences like 2, 4, 2, 4, 2, 4... which can go on forever if repeats are permitted.)

One way to characterize this problem is to look at it as a graph with a hundred vertices v[i]. Label v[i] with i, and connect v[i] and v[j], for i ≠ j, with an edge if either i is a multiple of j or j is a multiple of i. Then the problem asks for the longest path through some (or all, if it proves possible!) of the nodes of the graph following the edges with no node visited more than once. (If you solve the one-hundred node problem, the Riddler suggests you try the analogous problem with a thousand nodes.)

Alas, longest path is NP-hard. If you come up with an efficient way to solve this, your name will go down in computational complexity history. So I don't feel too bad starting out with a brute force depth-first search, and since I'm working to improve my Python skills, I start in Python.

The Riddler reports that one person ground through 30 million trials and came up with a sequence of length 55, which makes me suspect that our programs are similar. I started out with the obvious code using Python's built-in set and list types, and after over ten hours, that version had only managed to find a sequence of length 54. Then I started rewriting.
  • Step One: switch from the Python set type to using an integer (Python 3 has arbitrary precision integers, so yay). That did speed things up.
  • Step Two: rather than making lists come and go, accumulate the sequences in arrays that the recursive calls build up the sequence in. That sped things up even more.
The result: the length of known best results zips up to about the mid-40s very quickly indeed, but length 55 took at least two hours to appear. We're at four hours plus change, and 56 has yet to be seen. (UPDATE: showed up sometime between four and ten hours. Next version timestamps the output.)

Next stage will be a Go version to avoid interpreter overhead, and then dividing it up to take advantage of multiple cores, though given that the 55-number sequence is still very early on in the sequences starting with 1 (!?), I wonder how much good that will do. Here's that 55-number sequence, by the way:
1, 2, 4, 8, 16, 32, 96, 3, 6, 12, 24, 72, 36, 18, 54, 27, 81, 9, 99, 33, 66, 22, 44, 88, 11, 55, 5, 40, 80, 20, 60, 30, 90, 5, 15, 75, 25, 50, 100, 10, 70, 14, 56, 28, 84, 42, 21, 63, 7, 91, 13, 39, 78, 26, 52
The longest submitted sequences have length 77. More news as it happens. (Might experiment with how to pick the next item to try: the one with the most or least possible successors, perhaps? OTOH, if it were most, you'd always start with 1, and neither of the length 77 sequences the Riddler shows starts with 1.)


Thursday, June 15, 2017

Bikeshedding leap years

Darn it, I fell into the trap. An early exercise in Python at exercism.io wants a function that in Haskell would have the type Int -> Bool and indicate whether a year is a leap year (under Gregorian rules, ignoring when various locales made the switch to keep it simple).

Some submissions are from people not yet comfortable with assigning/returning Booleans other than the constants True or False, or maybe not comfortable with the lazy nature of Python's and and or. Their versions are full of if statements. I bashed out
def is_leap_year(year): return year % (400 if year % 100 == 0 else 4) == 0
and it worked fine... but then I fell in. How about
return year % 16 == 0 if year % 25 == 0 else year % 4 == 0
Nah... no need to make the next guy, who could be me, do the prime factorization of 100 again and figure out how that ties to the leap year rules? OK, here's one way to look at it...
return (year // 100 if year % 100 == 0 else year) % 4 == 0
i.e. on century boundaries, the century has to be divisible by four, otherwise the year has to be. Well, that's one way to look at it, but one that doesn't go with the definition as clearly as one would like...

...and then the path of premature optimization that Knuth warns against beckoned.
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
The idea: three fourths of numbers get ruled out by a check for divisibility by four, which can be done cheaply if the compiler or interpreter notice. (That works for any power of two, not just four, which with another nod to factorization means one could write
return year % 4 == 0 and (year % 100 != 0 or year % 16 == 0)
which means, if you're lucky, only doing one divisibility check with an actual divide.)

Final decision? Leave it alone

Tuesday, June 06, 2017

Restaurants shooting themselves in the foot

It's pet peeve time, and here it is: restaurants that display graphic images of their menu on the web. Sometimes it's a PDF made up of graphics, sometimes it's just graphics.

I wish I had a nickel for each restaurant I really like whose web site suffers from this problem... though perhaps I should shoot for more than that by offering my services to fix their web sites. A few examples:
How wrong is this? Let me count the ways...
  • You say you want people to be able to search your menu? Good luck (though someday the code that lets Google Translate read text from images may get good enough to deal with the wonky display fonts people tend to use on menus or text superimposed on food photos).
  • You say you want search engines to find your restaurant's web site? Good luck again, though as above, maybe someday...
  • You say you want your web site to support travelers looking for a place to eat even when they're on the road and only able to manage a 3G or 2G connection? Then why do you make them download large graphics files to look at your menu?
  • The blind eat, too... but restaurants whose web sites don't have text menus apparently don't care. (I did an "inspect" on a menu image; its ALT attribute was, I kid you not, "Picture".

Sunday, April 02, 2017

No tutorial, I swear...

After grinding through much of the "20 Intermediate Exercises" and just about all of the Monad Challenges, Monads make much more sense to me.

Therefore, I will follow the excellent advice of Stephen Diehl, and will not write a monad-analogy tutorial. Instead, I will say: do the 20 Intermediate Exercises, and take the Monad Challenges. To paraphrase Euclid, there is no royal road to Monads; the exercises and challenges take you down that road at whose end you'll see at least part of why Monads are so useful.

If you were trying to learn group theory and people were standing around you saying "groups are like the integers",  "groups are like Rubik's Cube", "groups are like m x n matrices", "groups are like baryons and mesons", your situation would be much like the student of Haskell amidst all the monad analogy tutorials. In a sense they're backwards. All those things are groups, just as burritos et al. can at least be thought of as monads--but not all groups are any one of those things.

Yeah, I suppose this is a meta-monad tutorial. Shame on me.

Friday, March 24, 2017

Flipping out

I came across the excellent Monad Challenges, a collection of exercises designed to take you through what have become some of the standard example monads and take you through the process that motivates monads as a way to handle these seemingly diverse data structures. Both the Monad Challenges and the 20 Intermediate Exercises are well worth your time. Doing them both is helping me a lot.

That said, they don't quite match up. 20IE's banana is flip bind and apple is flip ap. (This isn't unique; the also excellent Learning Haskell from first principles has as an exercise writing bind in terms of fmap and join, but the declaration it gives has the type of flip bind. The authors do point this out.) As a result, I find myself with something that there's got to be some way to simplify, of the form

foo = flip $ (flip mumble) . (flip frotz)

I'd like to think there's some sort of distributive-like law to be found and used here... watch this space.

Friday, March 03, 2017

Trust in SchÃļnfinkel

I'm working through the "20 Intermediate Exercises" that give Functors and Monads and such cute, non-threatening names and ask you to implement them. I've gotten most of the way through, with three or four that I'm stuck on--that's under the assumption that the inductive step from banana to banana2 will make the rest of the banana<n> obvious. (If you have sausage, it's easy to implement moppy, and vice versa, but avoiding circularity is the issue.)

So... I did a Bad Thing and looked at solutions a couple of people have posted, saying to myself, "OK, I'll look at those I've already done with an eye to more elegant expression, and look at the ones I'm having issues with and make sure I understand"... and came face to face with a question about composition.

<andy_rooney>Didja ever notice that the examples of composition you see are what we think of as functions with one argument?</andy_rooney> Well, one of the solutions involved composition that does not have that constraint, and that caused me much puzzlement until I remembered that we are, after all, using Haskell, where every function just has one argument. All it means is that the left-hand operand of (.) had better want to be passed a function...which is why the very next thing I typed at ghci just for the heck of it was stupid:

Prelude> :t ((+) . (-))
((+) . (-)) :: (Num a, Num (a -> a)) => a -> (a -> a) -> a -> a

Uh, yeah. I am pretty sure no function type is in Num, but what the heck, let's bind it to a name.

Prelude> let foo = ((+) . (-))

<interactive>:3:5:
    Non type-variable argument in the constraint: Num (a -> a)
    (Use FlexibleContexts to permit this)
    When checking that ‘foo’ has the inferred type
      foo :: forall a. (Num a, Num (a -> a)) => a -> (a -> a) -> a -> a

Didn't expect that. Perhaps ghci isn't quite as strict when you're just asking for the type.

UPDATE: I see why the solution didn't look type correct to me. I didn't understand Hindley-Milner well enough. It must be willing to do things like decide "to make this work, type a over here must map to type a -> b over there" in such a way that all is consistent. Now I know what to learn next.


Monday, January 23, 2017

Careful with that infinite list, Eugene...

Warning: this will give away one way to solve a certain low-numbered Project Euler problem.

Any Haskell book, blog, or tutorials you come across has a good chance of including the très ÊlÊgant Haskell one-liner for an infinite list containing the Fibonacci sequence:

fibs = 0 : 1 : (zipWith (+) fibs (tail fibs))

Thanks to laziness, it will only evaluate out to the last term we actually request... that was, long ago, my downfall. I wanted the terms no bigger than four million, so

filter (<= 4000000) fibs

right? Wrong. You and I know that the Fibonacci sequence is monotonically increasing, but filter doesn't, and it doesn't even notice the particular function we're filtering with to realize it could terminate thanks to monotonicity. So instead,

takeWhile (<= 4000000) fibs

is the way to go.

The particular problem has an additional constraint, because it only wants the even terms of the sequence no bigger than four million. Easy enough to do, just feed it through

filter even

and you're good. It did, though, occur to me: does the order of filtering matter? I mean, just plain

filter even fibs

is still an infinite list... but as long as we only actually evaluate finitely many values, we're safe.

filter even $ takeWhile (<= 4000000) fibs

and

takeWhile (<= 4000000) $ filter even fibs

both terminate and, as you'd expect, give the same result--but while the order doesn't matter to the result, it may matter for speed. Given that the first Fibonacci number is even and the next odd, the whole sequence is thus even, odd, odd, even, odd, odd, ... so

takeWhile (<= 4000000) $ filter even fibs

will only check a third as many values for being no more than four million.

Wednesday, October 05, 2016

TMTOWTDI, Haskell Style

I assure you there will be no further allusions to Korean earworms. That said, on to the subject at hand...

Remember the exercise in the online Haskell course that had several tests to filter out weak passwords, all of which had to pass for the fictitious system to allow a String value to be used as a password? I wanted to make it easy to change, so I wanted to take a [String -> Bool] and get a [Bool] I could apply and to for the final thumbs up/thumbs down decision.

The first step: roll my own, which has a pleasing symmetry with map if you write it as a list comprehension:

wonkyMap fs x = [f x | f <- fs]

Then I stumbled across Derek Wyatt's blog post about using sequence for the purpose. Life was good... and then I got Haskell Programming from first principles, and life got better, because its authors do a very good job of explaining the Applicative type class. Applicative defines two functions, pure and (<*>):

class Functor f => Applicative f where
    pure  :: a -> f a
    (<*>) :: f (a -> b) -> f a -> f b

Here f is a unary type constructor like Maybe or [] (both of which happen to be in Applicative). Being a Functor is a prerequisite for being an Applicative, as you can see from that first line. pure, given a value of type a, gives you back one of type f a. (<*>) is intended to act like function application, letting you take plain old a -> b values and apply some number of them (possibly zero--an empty list is a list, and Nothing is a Maybe (a -> b)) to some number of a values in an f a and get back an f b.

For our purposes, f is [], and if you think about it there's not much choice for what pure and (<*>) can be:

 instance Applicative [] where
    pure x = [x]
    fs <*> xs = [f x | f <- fs, x <- xs]

That's almost exactly what we want--the only thing we have to do is put our candidate password in a list (purify it?), like so:

s `satisfies` cs = and $ cs <*> [s]

So, for this exercise, Haskell is kind of Perl-like: TMTOWDI (there's more than one way to do it). Some would say this shows Haskell is Perl-like in a worse way, namely having weird unintelligible operators. I won't take a position, save to say that ghci makes it a lot easier than Perl (or APL, the original "WTF does this do?" language). :t is your friend!

Prelude> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

Now, are all three ways we've come up with equally good? Rolling your own function is a way to start, but why reinvent the wheel? sequence works, but

Prelude> :t sequence
sequence :: (Monad m, Traversable t) => t (m a) -> m (t a)

is far more general than (<*>), so that it takes more thought to realize it can be used this way. (<*>) more directly and specifically says it has to do with what we want, so I would argue that it is clearer.

One more thing: type classes don't just have functions; they have rules that those functions must follow. I urge you to track down the rules for Applicative and persuade yourself that [] satisfies them.

Monday, March 28, 2016

Haskell Tool Stack for Ubuntu 16.04

I've upgraded to Ubuntu 16.04 beta 2, and so far, as I've read elsewhere, it's working very nicely.

(One warning: if you have apcupsd installed, turn it off before upgrading! During the upgrade, something happens that causes the UPS battery to look fully discharged, and apcupsd will obligingly shut your system down... in mid-upgrade. Others have seen this when upgrading to 15.10.)

Now I'm reinstalling the various packages I had previously installed, but for FP Complete's Haskell Tool Stack, trying the Ubuntu instructions FP Complete gives (mutatis mutandis for the version) doesn't work, at least as I write. It looks like it's made its way into the Ubuntu repositories, so that sudo apt-get install haskell-stack will do the trick. I'm also quite pleased to see that 16.04 is actually up to date with respect to GHC. ghc --version shows it has 7.10.3. (Now to look to see whether it's up to snuff on the libghc* packages in the repositories...)

Sunday, January 10, 2016

A rather long blast from the past about recursion elimination and a bit of complexity theory

While going through old papers, I found something I wrote as a follow-up to an article by Aaron Banerjee in the February 1999 issue of the world of 68’ micros (you can find a somewhat mangled version online here). Both concern recursion elimination, with the well-known “eight queens” problem (place eight queens on a chess board so that none attacks any other) as an example.

I don’t remember whether I submitted it, or whether it was printed. I’m also embarrassed by the barely legible layout—large Helvetica with nearly no leading. To let me toss the printout and keep the text, and to make it easier to read (if only by me), here it is again, with formatting help from LibreOffice and editing for clarity and concision, not to mention fixing bugs I caused by not directly copying and pasting source code. I will come back and fix indentation on the BASIC09 code, after a little experimentation.

Recursion Elimination and the Eight Queens Problem

In the February 1999 the world of 68’ micros, Aaron Banerjee discussed recursive algorithms and how to implement them in languages that don’t support recursion. The Fibonacci sequence has a closed form, and can be turned into an iterative form, but in general, recursion elimination requires an explicit stack that takes the place of the stack typically used to implement function calls. The stack holds state information about parts of the problem to be done later, and actually corresponds to the local variables in a recursive call. Like Mr. Banerjee, we will use the eight queens problem as an example, and in addition to recursion elimination, we’ll try to speed up the check for valid arrangements.

BASIC09, unlike Color BASIC, directly supports recursion and procedures with local variables—you have to go outside the language itself, e.g. with a data module, to get the effect of global variables. Instead of doing that, we’ll have a top-level function declare variables for things like the chess board to pass to the procedure that does the real work.

procedure main
dim row, col, nsols: integer; board(8, 8): boolean
for row := 1 to 8
for col := 1 to 8
board(row, col) := false
next col
next row
nsols := 0
run queen8(1, board, nsols)
print nsols; “ solutions”
end

The board array is boolean because we’re just putting queens on the board; each square either has one (true) or it doesn’t (false). The parameters of queen8 are
  1. The column we’re about to try to put a queen in.
  2. The board itself, which is updated as queens are placed on it or removed.
  3. A count of the number of solutions we’ve seen so far; we added it to give a simple test. (A change from one run to another means we either broke something or fixed something.)
Our queen8 is turned around a bit from Mr. Banerjee’s. Rather than testing whether we have a full solution at entry (i.e. looking for col = 9), we check after placing a queen—it reduces the number of calls at the deepest level (and it means the test is for col = 8 rather than 9).

procedure queen8
param col: integer; board(8, 8): boolean; nsols: integer
dim row: integer; ok: boolean
for row := 1 to 8
run check(board, row, col, ok)
if ok then
board(row, col) := true
if col = 8 then
nsols := nsols + 1
run show(board)
else
run queen8(col + 1, board, nsols)
endif
board(row, col) := false
endif
next row
end

[Note from 2016: even if BASIC09 were in the optimization business, the call by reference would keep it from hoisting the evaluation of col = 8 out of the loop. We happen to know check won’t fiddle with col, so we could do it ourselves—but in a large program subject to change, that’s dangerous, and it can be easy to forget to write “run check(board, row + 0, col + 0, ok)” to force the issue. Given its constraints, call by reference was the way to go for BASIC09, but it’s not perfect.]

Now for the check procedure. Rather than iterating over each position to the “left” of the current column, we just look at the positions that can attack the tentatively-placed queen, which has the following advantages:
  • It looks at at most three positions per column rather than eight.
  • It can be done with one loop instead of two.
  • If we only look at attacking positions, we need only check the board for true, rather than checking whether the position is attacking.
Alas, BASIC09 evaluates both operands of boolean operations, so it’s a bit clumsy.

[Note from 2016: unlike the first version of this article, I will stick with the BASIC09-imposed layout. My experience with the Go programming language and the layout that gofmt imposes, thereby avoiding the endless bikeshedding and arguing over layout that afflicts some other languages, makes me appreciate BASIC09’s having established a standard in that regard almost three decades earlier.]

procedure check
param board(8, 8): boolean; row, col: integer; result: boolean
dim delta: integer
result := true
for delta := 1 to col – 1
exitif board(row, col – delta) then
result := false
endexit
if delta < row then
exitif board(row – delta, col – delta) then
result := false
endexit
endif
if row + delta <= 8 then
exitif board(row + delta, col – delta) then
result := false
endexit
endif
next delta
end

The show procedure is straightforward. To assist the eye in judging the queens’ positions, we print guide lines.

procedure show
param board(8, 8): boolean
dim row, col: integer
for row := 1 to 8
for col := 1 to 8
if board(row, col) then
print “Q”;
else
print “ ”;
endif
if col < 8 then
print “|”;
endif
next col
print
if row < 8 then
print “-+-+-+-+-+-+-+-”
next row
print
end

Now that we’ve shown the canonical recursive version, let’s eliminate the recursion from queen8, replacing it with a stack. (Happily, we know how big to make the stack; the recursion happens once per column, and there are eight of them on a chess board.) At any stage, there is only one thing to remember, namely the row we’re on. If you think about it, you’ll realize that the stack pointer is always the same as the column we’re on, so there’s no need to save it on the stack as Mr. Banerjee’s code does. (We lucked out on this one; in general, you will have to save the information associated with a pending piece of the problem.) We keep the same interface for now—admittedly from the start we’ve been exposing details we shouldn’t, and we should have put a layer around the board initialization and outer call to queen8. For now, though, we’re concentrating on recursion elimination.

procedure queen8
param col: integer; board(8, 8): boolean; nsols: integer
dim row, rowstack(8): integer; ok: boolean
row := 0
loop
while row < 8 do
row := row + 1
run check(board, row, col, ok)
if ok then
board(row, col) := true
if col = 8 then
nsols := nsols + 1
run show(board)
board(row, col) := false
else
rowstack(col) := row
row := 0
col := col + 1
endif
endif
endwhile
exitif col = 1 then endexit
col := col – 1
row := rowstack(col)
board(row, col) := false
endloop
end

This is less clear than the recursive version, so explanation is in order. The while loop looks for the first row we haven’t tried yet for which we can safely place a queen in that row on the current column. If there is one, we put a queen there, remember the row, and either we’re completely done (on column eight) or we move on to the next column. If there isn’t one, we leave the loop because we’re done with the current column. If that’s the first column, we’re completely done (see the exitif); otherwise, we back up a column, remember the row we were on there, and remove the queen we had on that row and column, so we can try the next possibility for that column.

I’ve yet to run the program on a CoCo. On the MM/1a, the recursive and non-recursive versions both take about two minutes to run. Curiously, over half the time is taken by show! If the body of show is edited out, the result runs in fifty seconds. On the MM/1b (aka AT306), the program runs in 1:15—five eighths of the time—so the screen I/O on the MM/1 and MM/1a has significant overhead.

One can do other things to speed the program up, but the point here is to show recursion and recursion elimination in a language with decent control structures that clarify what’s happening… oh, what the heck. I did it anyway, so why not write it up?

The revised check still takes time proportional to the column number, and the column number ranges over 1 to n, where n = 8, in the fashion of bubble sort, so overall we’re talking O(n2). We haven’t changed its complexity, we just cut it down by a constant factor. To really speed it up we have to make check run in constant time, doing the same amount of stuff no matter what row or column we’re looking at. How much stuff? Well, there are three paths down which an attack can come, so there ought to be a way to just do three tests: one for the row, and one for each diagonal. (We never put more than one queen in a column, so nothing can get us from that direction.) The board is small enough that we could keep a row as an integer with one bit per column, making the board one-dimensional and a check for a row attack is just checking for a non-zero value for that row. OK, what about the diagonals?

They need an array each, one for those going up and one for those going down (from the POV of increasing column number). Each array has fifteen elements, because there are fifteen diagonals of each kind, one per endpoint with the minimum column number (don’t count the middle corner twice, like I almost did). We could keep a count of how many there are… but we don’t have to! This is the eight queens problem. We cut off the search at the first conflict, so the elements can be boolean. For that matter, we needn’t play bit games with the board. There can be at most one piece in each row. We could get away with boolean there, too, if we didn’t want to display the solutions, but we do, so we’ll keep the column number of the queen in that row, or zero if we haven’t put a queen in that row… but that makes “board” a less-than-suggestive name. Let’s call it “column” instead.

With those changes, here are the new main, queen8, and show. We’ll add a little timing info to main. check is simple, and doesn’t need guards—we’ll never fall off the end of an array—so we’ll “inline” it by hand, and not write it separately from queen8. [Note from 2016: the code here isn’t best practice. We’re concentrating on the algorithm and data. Best practice would be to define an abstraction of the chess board that lets you get a new empty board, lets you attempt to place a queen on a square and reports success (if the square isn’t under attack) or failure (if it is), and lets you remove a queen from a square. That would let us make this kind of change without disturbing main or queen8. Sadly, BASIC09’s type statement reveals the innards of the type it defines, making “information hiding” a bit of a sham.]

procedure main
dim diag, row, nsols, column(8): integer
dim udiag(15), ddiag(15): boolean
dim t0, t1: string[32]
for row := 1 to 8
column(row) := 0
next row
for diag := 1 to 15
udiag(diag) := false
ddiag(diag) := false
next diag
t0 := date$
nsols := 0
run queen8(1, column, udiag, ddiag, nsols)
t1 := date$
print nsols; “solutions”
print “start at “; t0; “, end at “; t1
end


procedure queen8
param col, column(8): integer; udiag(15), ddiag(15): boolean; nsols: integer
dim row: integer
for row := 1 to 8
if column(row) = 0 and udiag(row + col – 1) and ddiag(8 + row – col) then
column(row) := col
udiag(row + col – 1) := false
ddiag(8 + row – col) := false
if col = 8 then
nsols := nsols + 1
run show(column)
else
run queen8(col + 1, column, udiag, ddiag, nsols)
endif
column(row) := 0
udiag(row + col – 1) := true
ddiag(8 + row – col) := true
endif
next row
end

procedure show
param column(8): integer
dim row, qpos: integer; rowstring: string[16]
rowstring := “ | | | | | | | ”
for row := 1 to 8
qpos := 2 * column(row) – 1
print left$(rowstring, qpos – 1); “Q”; right$(rowstring, 15 – qpos)
if row < 8 then
print “-+-+-+-+-+-+-+-”
endif
next row
print
end

This version runs on the MM/1b in twenty-four seconds, about three times faster. Allen Huffman kindly ran it under a CoCo 3 emulator (using TuneUp, which would give it an advantage over stock OS-9). With sync lock turned on to make it run at CoCo speed, it ran in about two and a half minutes; without sync lock, it ran in twenty-eight seconds. Eliminating recursion only saved about three seconds on the MM/1b. I instrumented queen8, and it proved to only be called 1,965 times, less than I would’ve thought. Moral of this part of the story: choose your data structures carefully; it can make a big difference. (Note that it lets us easily print each row with a single statement rather than a character at a time. I suspect that makes a big difference in BASIC09.)

Having gone this far, we might as well write it in Color BASIC. This is a recursive version—it has recursive GOSUBs, which is as close as you can get in Color BASIC. It lacks the comments that Mr. Banerjee’s code has, and which production code should have (though they eat valuable memory in Color BASIC, so the temptation to eliminate them there is strong). On the other hand, it follows the BASIC09 version closely, so one could argue that the BASIC09 is documentation for the Color BASIC. To conserve writer and reader sanity,
  • this is not crammed together the way Color BASIC code tends to be
  • the text is in lower case
The first solution, running on an actual CoCo, takes about thirty-five seconds to appear, and a complete run takes, as nearly as I can tell, ten minutes and forty-five seconds. I would recommend running the progam in WIDTH 80 mode.

100 dim cl(8), rs(8), ud(15), dd(15),
110 r$=” ! ! ! ! ! ! ! “
120 for i=1 to 8:cl(i)=0:next
130 for I=1 to 15:ud(i)=1:dd(i)=1:next
140 ns=0
150 co=1
160 gosub 200
170 sound 50,3
180 print ns;”solutions”
190 end

200 for ro=1 to 8
210 if cl(ro)<>0 or ud(ro+co-1)=0 or dd(8+ro-co)=0 then 250
220 cl(ro)=co:ud(ro+co-1)=0:dd(8+ro-co)=0
230 if co=8 then ns=ns+1:gosub 300:else rs(co)=ro:co=co+1:gosub 200:co=co-1:ro=rs(co)
240 cl(ro)=0:ud(ro+co-1)=1:dd(8+ro-co)=1
250 next
260 return

300 cls
310 for rn=1 to 8
320 qp=2*cl(rn)-1
330 print left$(r$,qp-1);”Q”;right$(r$,15-qp)
340 if rn<8 then print “-+-+-+-+-+-+-+-”
350 next
360 return

Recursion is an important concept with many applications, not all of which have closed-form solutions. For example, consider plotting a function f on an interval [a,b] if all you can do on the display is draw line segments. Pick a point c in (a, b) and check whether (c, f(c)) is “close enough” to being on the line segment joining (a, f(a)) and (b, f(b)). If it is, draw that segment. Otherwise, recur on [a, c] and then on [c, b]. (Numerical methods students will recognize this as “adaptive trapezoidal integration”.) By all means, learn to use recursion, and learn how to avoid it or implement it via an explicit stack as Mr. Banerjee describes. [Note from 2016: It can come in handy for any language—for example, it is very common for embedded systems to flatly forbid recursion, whatever the language, for fear of stack overflow.]

Riddler Classic, May 23, 2020—Holy Mackerel!

Another one using Peter Norvig's word list . It turns out that the word "mackerel" has a curious property: there is exactly ...