A mail from Eric Dobbs on 2021/12/29 08:44
Hello Alain. I've enjoyed following some of these conversations you've had with Ward and Thomson. Tonight I had a difficult time porting some LOGO code to lambdatalk. I think I'm missing something basic. Some years ago I volunteered with a summer programming camp and helped an advanced 5th grader learn some fun things with Microworlds Logo. Short story is that we started with some found code for the Von Koch snowflake. We generalized the code to remove some repetition. That allowed us to discover similarity to the Peano curve. Longer story is over here: http://dobbse.net/thinair/2008/12/logo-fractals-recursion.html I was not able to get DrawFractalLine ported to lambdatalk. I did find an implementation of logo where I could share a running variant of the code "Sam" and I worked on. Here's the logo interpreter: https://www.calormen.com/jslogo/ Should be able to just copy & paste the following code to see these visually.to DrawFractalLine :commands :level :length ( define "walk [ [ command ] [ DrawFractalLine :commands (sum -1 :level) (quotient :length 3.0) if not empty? :command [ run :command ] ] ] ) ifelse :level < 1 [ forward :length ] [ foreach "walk :commands ] end to VonKoch :level :length DrawFractalLine [ [ left 60 ] [ right 120 ] [ left 60 ] [ ] ] :level :length end to Peano :level :length DrawFractalLine [ [ left 90 ] [ right 90 ] [ right 90 ] [ right 90 ] [ left 90 ] [ left 90 ] [ left 90 ] [ right 90 ] [ ] ] :level :length end to SamCurve :level :length DrawFractalLine [ [ left 90 ] [ right 90 ] [ right 90 ] [ ] [ right 90 ] [ right 90 ] [ right 90 ] [ forward (2 * :length / 3) ] ] :level :length end clearscreen rt 90 penup setpos [ 0 175 ] pendown VonKoch 3 200 penup setpos [ 0 50] pendown Peano 3 200 penup setpos [ 0 -150] pendown SamCurve 3 120It seems like this should all code up pretty nicely in lambdatalk too, but I couldn't figure out how to pass in a list of commands and get them applied over a list in the recursion. I tried using in various ways but couldn't quite get it working. Take care. -Eric
Here is a translation from LOGO to lambdatalk.
{def koch {lambda {:level :length} {if {< :level 1} then M:length else {koch {- :level 1} {/ :length 3}} T-60 {koch {- :level 1} {/ :length 3}} T120 {koch {- :level 1} {/ :length 3}} T-60 {koch {- :level 1} {/ :length 3}} }}} -> koch {def peano {lambda {:level :length} {if {< :level 1} then M:length else {peano {- :level 1} {/ :length 3}} T-90 {peano {- :level 1} {/ :length 3}} T90 {peano {- :level 1} {/ :length 3}} T90 {peano {- :level 1} {/ :length 3}} T90 {peano {- :level 1} {/ :length 3}} T-90 {peano {- :level 1} {/ :length 3}} T-90 {peano {- :level 1} {/ :length 3}} T-90 {peano {- :level 1} {/ :length 3}} T90 {peano {- :level 1} {/ :length 3}} }}} -> peano {def sam {lambda {:level :length} {if {< :level 1} then M:length else {sam {- :level 1} {/ :length 3}} T-90 {sam {- :level 1} {/ :length 3}} T90 {sam {- :level 1} {/ :length 3}} T90 {sam {- :level 1} {/ :length 3}} T0 {sam {- :level 1} {/ :length 3}} T90 {sam {- :level 1} {/ :length 3}} T90 {sam {- :level 1} {/ :length 3}} T90 {sam {- :level 1} {/ :length 3}} M{* 2 {/ :length 3}} }}} -> sam
It's easy to understand but there are lots of repetitions which should be replaced by a loop. See section 2)
Drawings are called like this:
{svg {@ width="580" height="580" style="box-shadow:0 0 8px #000;"} {polyline {@ points="{turtle 100 100 0 {koch 3 300}}" stroke="#f00" fill="transparent"}} {polyline {@ points="{turtle 280 250 0 {peano 1 300}}" stroke="#888" fill="transparent" stroke-width="5"}} {polyline {@ points="{turtle 280 250 0 {peano 3 300}}" stroke="#0f0" fill="transparent"}} {polyline {@ points="{turtle 400 10 0 {sam 1 200}}" stroke="#888" fill="transparent" stroke-width="5"}} {polyline {@ points="{turtle 400 10 0 {sam 3 200}}" stroke="#00f" fill="transparent"}} }
Note that such a code is the immediate translation of standard HTML/CSS/SVG syntaxes. Details of svg, polyline, stroke, ... can be found anywhere on the net. For instance
{polyline {@ points="{turtle 280 250 0 {peano 3 300}}" stroke="#0f0" fill="transparent"}}
could be written
<polyline points="{turtle 280 250 0 {peano 3 300}}" stroke="#0f0" fill="transparent" />
It's a matter of choice. The result is below
Some more curves (and better explanations) can be seen in koch, hilbert, peano, sierpinsky, fern, dragon, fractal_tree, stars, ...
This is the DrawFractalLine Logo function in http://dobbse.net/thinair
to DrawFractalLine :commands :level :length ( define "walk [ [ command ] [ DrawFractalLine :commands (sum -1 :level) (quotient :length 3.0) if not empty? :command [ run :command ] ] ] ) ifelse :level < 1 [ forward :length ] [ foreach "walk :commands ] end
This recursive function contains a loop, foreach, and the goal is to put in such a loop the repetitions found in lambdatalk functions above and to hide recursive details.
{def fractal {def fractal.repeat {lambda {:level :length :curve :arr} {if {A.empty? :arr} then else {fractal :level :length :curve} {A.first :arr} {fractal.repeat :level :length :curve {A.rest :arr}} }}} {lambda {:level :length :curve} {if {< :level 1} then M:length else {fractal.repeat {- :level 1} {/ :length 3} :curve {:curve :length}} }}} -> fractal
We can understand that the line
{fractal :level :length :curve} {A.first :arr}
with
:level = {- :level 1} :length = {/ :length 3} :curve = koch :arr = [T-60,T120,T-60,T0]
generates the following four lines
{koch {- :level 1} {/ :length 3}} T-60 {koch {- :level 1} {/ :length 3}} T120 {koch {- :level 1} {/ :length 3}} T-60 {koch {- :level 1} {/ :length 3}}
in the koch function shown in the first section.
See pages lambda, coding, oops, maxwell_equations for detailed explanations on lambdas.
{def KOCH {A.new T-60 T120 T-60 T0}} -> KOCH {def PEANO {A.new T-90 T90 T90 T90 T-90 T-90 T-90 T90 T0}} -> PEANO {def SAM {lambda {:length} {A.new T90 T-90 T-90 T0 T-90 T-90 T-90 M{* 2 {/ :length 3}}}}} -> SAM {def VKZIG {A.new T-30 T120 T-120 T30 T0}} -> VKZIG
See page WAPL for explanations about arrays and other data structures. An alternative to arrays could be to use sequences simplifying the above definitions, for example {def VKZIG T-30 T120 T-120 T30 T0}
{svg {@ width="580" height="580" style="box-shadow:0 0 8px #000;"} {polyline {@ points="{turtle 100 10 0 {fractal 1 300 KOCH}}" stroke="#888" fill="transparent" stroke-width="3"}} {polyline {@ points="{turtle 100 10 0 {fractal 3 300 KOCH}}" stroke="#f00" fill="transparent" stroke-width="1"}} {polyline {@ points="{turtle 250 250 0 {fractal 1 300 PEANO}}" stroke="#888" fill="transparent" stroke-width="3"}} {polyline {@ points="{turtle 250 250 0 {fractal 3 300 PEANO}}" stroke="#0f0" fill="transparent" stroke-width="1"}} {polyline {@ points="{turtle 200 160 90 {fractal 1 200 SAM}}" stroke="#888" fill="transparent" stroke-width="3"}} {polyline {@ points="{turtle 200 160 90 {fractal 3 200 SAM}}" stroke="#00f" fill="transparent" stroke-width="1"}} {polyline {@ points="{turtle 20 20 45 {fractal 4 300 VKZIG}}" stroke="#000" fill="transparent" stroke-width="2"}} }
The fractal.repeat recursive function could be replaced by an iterative version using the {S.map function a sequence of words} lambdatalk loop structure:
{def fractal.repeat {lambda {:level :length :curve :arr} {S.map {{lambda {:level :length :curve :arr :i} {fractal :level :length :curve} {A.get :i :arr} } :level :length :curve :arr} {S.serie 0 {- {A.length :arr} 1}} }}}
You can try it, it works fine, but I think that the recursive version makes code more consistent and easier to understand.
See eric_dobbs2 for another version. Maybe the best.
Your opinion is welcome.
Alain Marty | 2021/12/31 - 2022/01/01