Re: Factor

Factor: the language, the theory, and the practice.

Tak Function

Wednesday, July 23, 2025

#math

The Tak function is a recursive function, named after Ikuo Takeuchi, used sometimes as a programming language benchmark. On the Concatenative Wiki, I noticed a page with concatenative implementations of the Tak function, and wanted to explore some different ways to build it in Factor.

def tak(x: int, y: int, z: int) -> int:
    if y < x:
        return tak(
            tak(x - 1, y, z),
            tak(y - 1, z, x),
            tak(z - 1, x, y)
        )
    else:
        return y

Note: there are two variants of this function defined, some return z instead of y.

Some parameters cause an extremely large number of recursions. For example, tak(18, 12, 25) makes 76,527,789 function calls! Measuring the execution time demonstrates that:

In [23]: %time tak(18, 12, 25)
CPU times: user 1.79 s, sys: 4.89 ms, total: 1.79 s
Wall time: 1.79 s

Out [23]: 25

A direct translation of the Python code might look like this:

:: tak ( x y z -- result )
    y x < [
        x 1 - y z tak
        y 1 - z x tak
        z 1 - x y tak
        tak
    ] [ y ] if ;

And we can see that it works and is almost 6x faster than Python.

IN: scratchpad [ 18 12 25 tak ] time .
Running time: 0.307333458 seconds

25

But, we could get all stacky and build it like this:

: tak ( x y z -- result )
    2over > [
        [ [ 1 - ] 2dip tak ] 3keep
        [ [ 1 - ] dip rot tak ] 3keep
        1 - -rot tak tak
    ] [ drop nip ] if ;

Or, cleavy and build it like this:

: tak ( x y z -- result )
    2over > [
        {
            [ [ 1 - ] 2dip tak ]
            [ [ 1 - ] dip rot tak ]
            [ 1 - -rot tak ]
        } 3cleave tak
    ] [ drop nip ] if ;

Or, composey and build it like this:

: tak ( x y z -- result )
    2over > [
        [ ] [ rot ] [ -rot ] [ '[ @ [ 1 - ] 2dip tak ] ] tri@ 3tri tak
    ] [ drop nip ] if ;

Or, reduce some unnecessary recursion with a more efficient version:

:: tak ( x! y! z! -- result )
    [ x y > ] [
        x y :> ( oldx oldy )
        x 1 - y z tak x!
        y 1 - z oldx tak y!
        x y <= [ z 1 - oldx oldy tak z! ] unless
    ] while y ;

But, it turns out there is a simpler solution:

:: tak ( x y z -- result )
    {
        { [ y x >= ] [ y ] }
        { [ y z <= ] [ z ] }
        [ x ]
    } cond ;

Which could be written more concisely in one line:

:: tak ( x y z -- result )
    y x >= [ y ] [ y z <= z x ? ] if ;

And that is the fastest of all:

IN: scratchpad [ 18 12 25 tak ] time .
Running time: 0.000073916 seconds

25

Fun.