Fractals in 10 minutes No. 6: Turtle Snowflake

I didn’t write this one, but I found it’s simplicity and availability so compelling, I couldn’t just not write about it.
In a reddit post from a while ago, some commenter named jfedor left the following comment:

A little known fact is that you can do the following on any standard Python installation:

from turtle import *
 
def f(length, depth):
   if depth == 0:
     forward(length)
   else:
     f(length/3, depth-1)
     right(60)
     f(length/3, depth-1)
     left(120)
     f(length/3, depth-1)
     right(60)
     f(length/3, depth-1)
 
f(500, 4)

If you copy paste, it’s a fractal in less than a minute. If you type it yourself, it’s still less than 10. And it’s something you can show a kid. I really liked this one.

This entry was posted in computer science, Fractals, Python and tagged , , . Bookmark the permalink.

5 Responses to Fractals in 10 minutes No. 6: Turtle Snowflake

  1. Beni Cherniavsky says:

    Cool!
    I remember doing similar fractals as execin SICP
    [ http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-15.html#%_sec_2.2.4 ]
    which takes a functional approach.
    So I couldn’t resist to try rewriting it:
    [python]
    def snowflake(length, line=forward, *rest):
    line(length/3, *rest)
    right(60)
    line(length/3, *rest)
    left(120)
    line(length/3, *rest)
    right(60)
    line(length/3, *rest)
    [/python]

    Then try:

    [python]
    snowflake(400)
    snowflake(400, snowflake)
    snowflake(400, snowflake, snowflake)
    [/python]

    etc.

    The purpose of the exercise was to get away from recursion with terminating condition.
    Recursion is cool, but there is a constructive process here that I want to capture directly:
    Draw the _/\_ shape, but use $subshape instead of straight lines.

    But of course “line=forward, *rest” is way too much magic for a newbie.

    “snowflake(400, lambda length: snowflake(length))” is even worse.

    So, how can I nicely capture the construction without recursion? Aha!

    [python]
    def snowflake(length):
    yield length/3
    right(60)
    yield length/3
    left(120)
    yield length/3
    right(60)
    yield length/3

    for a in snowflake(400):
    for b in snowflake(a):
    forward(b)
    [/python]

    What do you think of this?
    It’s as close as Python gets to code blocks – a function with fill-in spaces, “do this where I said yield”.
    Can this work better then recursion for newbies?
    Can we explain yield/for simply enough?

    [I suspect people who know some programming would be more daunted than newbies, because yield is powerful and strange - but newbies don't have to know that :-]

    P.S. http://mathworld.wolfram.com/KochSnowflake.html

  2. Beni Cherniavsky says:

    p.s. I don’t know if WordPress can be configured that way,
    but in a Pythonic blog, you really want to preserve whitespace in comments.

  3. lorg says:

    1. I fixed the python bits in your comment. Writing Python in comments is easy: just add [ python ] and [ / python ] blocks (just without the spaces).

    2. What you suggest is very reminiscent of lstrings, check out http://www.algorithm.co.il/blogs/index.php/projects/lstrings/ , and older fractal links in my blog for more info.

    I’m not sure if it’s clearer than recursion. If presented correctly, recursion may be very intuitive. It might even be explained intuitively using this example! It works much better than sorting :)
    Regarding usage of yield – I’d rather avoid it I think, because then you have to do a lot of hand waving to explain it to newbies (you can’t be accurate).

    By the way, I really liked the line=forward trick. Maybe the names should be different or the *rest should be changed, but it really explains the idea behind the recursion.

  4. Pingback: Python分形–Turtle Snowflake | Code之行人

  5. Chris Wong says:

    Hey, I came up with some very similar code a few days ago:

    def koch(depth, size):
        if depth == 0:
            forward(size)
        else:
            recurse = lambda: koch(depth-1, size/3)
            recurse()
            left(60)
            recurse()
            right(120)
            recurse()
            left(60)
            recurse()
     
    koch(3, 3**4)

    Guess all that Haskell trained me well :)