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.

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

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.

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.

Chris Wong says

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

Guess all that Haskell trained me well :)

mukes says

don’t get it, where to paste it? i pasted it in the terminal as well as in a file.. there’s no output at all.. what am i doin’ wrong, please? i’m new on python and familiar mostly to delphi, so i wonder how it can be drawn, because there seems to be no routine of drawing..?

Yehuda Katz says

I’m not afraid of recursion, as my following program uses only basic methods. Here it is:

[python]

from turtle import *

ht(); speed(0)

def fractal(length, depth):

if depth < 1: fd(length) # base case

else:

fractal(length, depth-1); rt(60)

fractal(length, depth-1); lt(120)

fractal(length, depth-1); rt(60)

fractal(length, depth-1)

for i in range(6): # closing the flake

fractal(5, 3); rt(60)

[/python]