Came across the following LOGO code one day:
; Koch Snowflake to KochIsland :size :level repeat 3 [KSide :size :level rt 120] end to KSide :size :level if :level = 0 [fd :size stop] KSide :size / 3 :level - 1 lt 60 KSide :size / 3 :level - 1 rt 120 KSide :size / 3 :level - 1 lt 60 KSide :size / 3 :level - 1 end
It draws Koch snowflake. Don't matter if you don't know (or care about) LOGO, I was just impressed with its briefness, as well as its descriptiveness.

So, I thought, just for fun, I could probably do something more or less as succinct in Perl (which, after all, is known for its terseness).

I came up with the following three versions (in the order of attempt):

Closure version:
#! /usr/local/bin/perl -w use strict; use GD; # ----------------------------------------------------------- use constant PI => 3.14159265359; # ----------------------------------------------------------- # turtle as in LOGO's turtle # Usage: $turtle = turtle(); # $turtle->($turn_right_x_degree, $forward_x_points); # $xy = $turtle->(); sub turtle { my ($h, $xy) = (0, [[0],[0]]); # h = heading (0 - north, 90 - +east, etc) return sub { $h = $h + (shift || 0); # accumulative turns in degree my $d = shift || 0; # distance $xy->[0][scalar(@{$xy->[0]})] = $d*sin(PI*$h/180) + $xy->[0][$ +#{@{$xy->[0]}}]; $xy->[1][scalar(@{$xy->[1]})] = $d*cos(PI*$h/180) + $xy->[1][$ +#{@{$xy->[1]}}]; return $xy; }; } # ----------------------------------------------------------- # Koch Snowflake sub koch { my ($turtle, $d, $level) = @_ ; if ($level==0) {$turtle->(0,$d); return 1;} $turtle->( 0,0); koch($turtle,$d/3,$level-1); $turtle->(-60,0); koch($turtle,$d/3,$level-1); $turtle->(120,0); koch($turtle,$d/3,$level-1); $turtle->(-60,0); koch($turtle,$d/3,$level-1); } # ----------------------------------------------------------- my $turtle = turtle(); map {$turtle->(120, 0); koch($turtle, 170, 4);} 0..2; # ----------------------------------------------------------- plotxy($turtle->(), 'koch.jpg'); # ----------------------------------------------------------- # ----------------------------------------------------------- # Minkowski Island sub minkowski { my ($turtle, $d, $level) = @_ ; if ($level==0) {$turtle->(0,$d); return 1;} minkowski($turtle,$d/4,$level-1); $turtle->(-90,0); minkowski($turtle,$d/4,$level-1); $turtle->( 90,0); minkowski($turtle,$d/4,$level-1); $turtle->( 90,0); minkowski($turtle,$d/4,$level-1); minkowski($turtle,$d/4,$level-1); $turtle->(-90,0); minkowski($turtle,$d/4,$level-1); $turtle->(-90,0); minkowski($turtle,$d/4,$level-1); $turtle->( 90,0); minkowski($turtle,$d/4,$level-1); } # ----------------------------------------------------------- $turtle = turtle(); map {$turtle->(90,0); minkowski($turtle, 150, 3);} 0..3; # ----------------------------------------------------------- plotxy($turtle->(), 'minkowski.jpg'); # ----------------------------------------------------------- # ----------------------------------------------------------- # Dragon Curve sub dragon1 { my ($turtle, $d, $level) = @_ ; if ($level==0) {$turtle->(0,$d); return 1;} dragon($turtle,$d*0.707,$level-1); $turtle->(-90,0); dragon1($turtle,$d*0.707,$level-1); } sub dragon { my ($turtle, $d, $level) = @_ ; if ($level==0) {$turtle->(0,$d); return 1;} dragon($turtle,$d*0.707,$level-1); $turtle->(90,0); dragon1($turtle,$d*0.707,$level-1); } # ----------------------------------------------------------- $turtle = turtle(); dragon($turtle, 150, 12); # ----------------------------------------------------------- plotxy($turtle->(), 'dragon.jpg'); # ----------------------------------------------------------- # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Sub for Plotting # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # sub min {(sort {$a<=>$b} @_ )[0];} # ---------------------------------------------------------------- sub max {(sort {$a<=>$b} @_ )[-1];} # ---------------------------------------------------------------- sub plotxy { my $xy = shift; my $file = shift; my $scale = shift || 1; # ------------------------------------------------------- my @x = @{$xy->[0]}; my @y = @{$xy->[1]}; my $x_min = min(@x); my $y_min = min(@y); map {$x[$_]=$scale*($x[$_]-$x_min)} 0..$#x; map {$y[$_]=$scale*($y[$_]-$y_min)} 0..$#y; my $x_width = max(@x) - 0; my $y_width = max(@y) - 0; # ------------------------------------------------------- my $image = new GD::Image($x_width,$y_width); my $white = $image->colorAllocate(255,255,255); my $black = $image->colorAllocate(0,0,0); $image->transparent($white); $image->interlaced('true'); $image->line($x[$_-1],$y[$_-1],$x[$_],$y[$_],$black) for 1..$#x; $image = $image->copyFlipVertical() ; open(IMG, ">$file"); binmode IMG; print IMG $image->jpeg; }

OO version:
# Turtle as in LOGO's Turtle {package turtle; use constant PI => 3.14159265359; sub new {return bless({h=>0,xy=>[[0],[0]]});} sub rt {$_[0]->{h}=$_[0]->{h}+$_[1];} # right turn by x degrees sub fd { # forward by x points my ($h, $xy, $d) = ($_[0]->{h}, $_[0]->{xy}, $_[1]); $xy->[0][scalar(@{$xy->[0]})] = $d*sin(PI*$h/180) + $xy->[0][$ +#{@{$xy->[0]}}]; $xy->[1][scalar(@{$xy->[1]})] = $d*cos(PI*$h/180) + $xy->[1][$ +#{@{$xy->[1]}}]; $_[0]->{xy} = $xy; return $xy; } } # ----------------------------------------------------------- # Koch Snowflake sub koch { my ($turtle, $d, $level) = @_ ; if ($level==0) {$turtle->fd($d); return 1;} $turtle->rt( 0); koch($turtle, $d/3,$level-1); $turtle->rt(-60); koch($turtle, $d/3,$level-1); $turtle->rt(120); koch($turtle, $d/3,$level-1); $turtle->rt(-60); koch($turtle, $d/3,$level-1); } # ----------------------------------------------------------- my $turtle = turtle->new(); map {$turtle->rt(120); koch($turtle, 170, 4);} 0..2; # ----------------------------------------------------------- plotxy($turtle->{xy}, 'koch.jpg'); # ----------------------------------------------------------- # Minkowski Island sub minkowski { my ($turtle, $d, $level) = @_ ; if ($level==0) {$turtle->fd($d); return 1;} minkowski($turtle,$d/4,$level-1); $turtle->rt(-90); minkowski($turtle,$d/4,$level-1); $turtle->rt( 90); minkowski($turtle,$d/4,$level-1); $turtle->rt( 90); minkowski($turtle,$d/4,$level-1); minkowski($turtle,$d/4,$level-1); $turtle->rt(-90); minkowski($turtle,$d/4,$level-1); $turtle->rt(-90); minkowski($turtle,$d/4,$level-1); $turtle->rt( 90); minkowski($turtle,$d/4,$level-1); } # ----------------------------------------------------------- $turtle = turtle->new(); map {$turtle->rt(90); minkowski($turtle, 150, 3);} 0..3; # ----------------------------------------------------------- plotxy($turtle->{xy}, 'minkowski.jpg'); # ----------------------------------------------------------- # ----------------------------------------------------------- # Dragon Curve sub dragon1 { my ($turtle, $d, $level) = @_ ; if ($level==0) {$turtle->fd($d); return 1;} dragon($turtle,$d*0.707,$level-1); $turtle->rt(-90); dragon1($turtle,$d*0.707,$level-1); } sub dragon { my ($turtle, $d, $level) = @_ ; if ($level==0) {$turtle->fd($d); return 1;} dragon($turtle,$d*0.707,$level-1); $turtle->rt(90); dragon1($turtle,$d*0.707,$level-1); } # ----------------------------------------------------------- $turtle = turtle->new(); dragon($turtle, 150, 12); # ----------------------------------------------------------- plotxy($turtle->{xy}, 'dragon.jpg');

"Simple" version:
use constant PI => 3.14159265359; # ----------------------------------------------------------- # Turtle Graph my $h=0; my @x=(0); my @y=(0); sub rt {$h = $h + shift;} sub fd { $x[scalar @x] = $_[0]*sin(PI*$h/180) + $x[$#x]; $y[scalar @y] = $_[0]*cos(PI*$h/180) + $y[$#y]; } sub reset_xy {$h=0; @x=(0); @y=(0);} # ----------------------------------------------------------- # ----------------------------------------------------------- # Koch Snowflake sub koch { my ($d, $level) = @_ ; if ($level==0) {fd($d); return 1;} rt( 0); koch($d/3,$level-1); rt(-60); koch($d/3,$level-1); rt(120); koch($d/3,$level-1); rt(-60); koch($d/3,$level-1); } # ----------------------------------------------------------- map {rt(120); koch(170, 4);} 0..2; # ----------------------------------------------------------- plotxy([\@x,\@y], 'koch.jpg'); # ----------------------------------------------------------- # ----------------------------------------------------------- # Minkowski Island sub minkowski { my ($d, $level) = @_ ; if ($level==0) {fd($d); return 1;} minkowski($d/4,$level-1); rt(-90); minkowski($d/4,$level-1); rt( 90); minkowski($d/4,$level-1); rt( 90); minkowski($d/4,$level-1); minkowski($d/4,$level-1); rt(-90); minkowski($d/4,$level-1); rt(-90); minkowski($d/4,$level-1); rt( 90); minkowski($d/4,$level-1); } # ----------------------------------------------------------- reset_xy(); # if necessary map {rt(90); minkowski(150, 3);} 0..3; # ----------------------------------------------------------- plotxy([\@x,\@y], 'minkowski.jpg'); # ----------------------------------------------------------- # ----------------------------------------------------------- # Dragon Curve sub dragon1 { my ($d, $level) = @_ ; if ($level==0) {fd($d); return 1;} dragon($d*0.707,$level-1); rt(-90); dragon1($d*0.707,$level-1); } sub dragon { my ($d, $level) = @_ ; if ($level==0) {fd($d); return 1;} dragon($d*0.707,$level-1); rt(90); dragon1($d*0.707,$level-1); } # ----------------------------------------------------------- reset_xy(); # if necessary dragon(150, 12); # ----------------------------------------------------------- plotxy([\@x,\@y], 'dragon.jpg');

All three versions (Closure, OO, "Simple") produce Koch, Minkowski and Dragon curves (which you can view here). (Weird I didn't think of "Simple" version first, but last. Must be due to OOP--Object-Oriented Poisoning.)

Short Code?       A long while back, some monks here wrote some codes that generate Mandelbrot set in about 150 characters! So, I guess, some folks here could probably write some pretty terse code to generate those fractal curves, without relying (too much) on external modules (otherwise it would be like going to a high school algebra exam carrying Mathematica with you).

Fast Code?       In case you're interested in the relative speed. The Closure, OO and "Simple" versions took about 2.4, 1.5 and 1.3 seconds to run respectively. Is closure supposed to be slow or is it just my code? Any trick to speed things up?

(O, if you know of Memoize module, I tried it. Crashed on Minkowski curve.)

Thanks.

In reply to Fractal Curves: Short & Fast Codes? by chunlou

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.