An Interest In:
Web News this Week
- April 19, 2024
- April 18, 2024
- April 17, 2024
- April 16, 2024
- April 15, 2024
- April 14, 2024
- April 13, 2024
100 Languages Speedrun: Episode 33: Logo
Logo is a programming language all the way from the 1960s aimed at teaching kids programming. The most notable feature of Logo are "turtle graphics" - simple commands that draw lines on screen by moving an imaginary "turtle".
One issue with covering Logo is that it's meant for interactive use in some Logo GUI environment, and these are platform specific and don't last very long, so every variant of Logo will be quite different. And it's not just their fancy features like 3D graphics, interactivity, and so on. Even very basic commands like like changing color are going to be different in each Logo.
I'll be using in-browser papert Logo, so all examples will work in papers. Different Logo implementations will need some adjustments. I'll try to mention when something is implementation-specific.
I'll post a few of the pictures generated by the programs - if you want to see some that I skipped, just try them out in papert.
Other Logo implementations I'll mention are SLogo (also in-browser), and ACSLogo for OSX.
Basic Drawing Commands
We're not printing anything, we're controlling a "turtle". A turtle has position on a screen as well as orientation.
To draw a square we can tell the turtle to move forward 50 steps, turn 90 degrees to the right, four times:
; Squareforward 50right 90forward 50right 90forward 50right 90forward 50right 90
Line comments use ;
.
As these commands are a bit long, and we'll be using them all the time, there are shorter versions too. fd
for forward
, rt
for right
and lt
for left
:
; Trianglefd 60rt 120fd 60rt 120fd 60rt 120
Logo implementation differences
We can also control color and thickness of lines. In papert we can use color [R G B]
and penwidth WIDTH
. For very simple loops we can do repeat N [COMMANDS]
:
; Blue Hexagon - Papertcscolor [200 200 255]penwidth 4repeat 6 [fd 60 rt 60]ht
Logo programs tend to show the turtle on the screen. To show or hide the turtle we can use st
and ht
commands.
Logo doesn't clear the screen by default when you start the program, so if you want to do so, you should use cs
command explicitly.
Anyway, here's the same program in JSLogo, which has RGB 0-100 instead of 0-255, and slightly different commands:
; Blue Hexagon - JSLogocssetpencolor [80 80 100]setpensize 4repeat 6 [fd 60 rt 60]ht
And here's more traditional ACSLogo, which only has fixed colors 0-15, and doesn't have comments:
cssetpencolor 15setpenwidth 4repeat 6 [fd 60 rt 60]ht
As you can see, there's zero hope of writing any kind of "portable" Logo programs.
Procedures
We can define procedures with to name ... end
. Like this draws three letters I:
to draw_i ; draw line forward 10 penup ; go to next character right 90 forward 5 right 90 forward 10 ; reset to facing up, pen down pendown right 180endcsrepeat 3 [draw_i]
To move the turtle without touching the screen, we can use penup
and pendown
commands (or pu
and pd
).
Step by step:
- turtle faces up, at some point, let's say (100, 100), Logo normally doesn't use coordinates at all, but let's say these are normal computer graphics coordinates (X points right, Y points down). Pen is down.
forward 10
makes turtle draw line up to(100, 90)
penup
ends drawing, but we still need to position turtle at the next letterright 90
andforward 5
makes turtle turn clockwise by 90 degrees (so it's pointing right for us) and advance to(105, 90)
without drawing.right 90
andforward 10
makes turtle turn clockwise by 90 degrees (so it's pointing down for us) and advance to(105, 100)
without drawing.pendown
andright 180
makes turtle press the pen down turn clockwise by 180 degrees (so it's pointing up for us), so we end up 5 pixels to the right from where we started, in same orientation and pen state
Fizz
You can probably see where this is going, here's a program that says FIZZ
three times:
to draw_i fd 10 ; main stroke ; go to next character pu rt 90 fd 5 rt 90 fd 10 ; reset pen state pd rt 180endto draw_f fd 10 ; main line rt 90 fd 5 ; top stroke pu rt 180 fd 5 lt 90 fd 5; move to next stroke pd lt 90 fd 5; middle stroke ; go to next character pu fd 5 rt 90 fd 5 ; reset pen state pd rt 180endto draw_z rt 90 fd 5; bottom line pu rt 180 fd 5 ; return pd rt 120 fd 11 ; diagonal stroke lt 120 fd 5 ; top line ; advance to next character pu rt 180 fd 5 rt 120 fd 11 lt 120 fd 10 ; reset pen state pd lt 90endto draw_fizz draw_f draw_i draw_z draw_zendcsrepeat 3 [draw_fizz]
I could explain it step by step, but it's probably easier if you try to run it in Papert using "run slowly" button to see how turtle moves step by step.
As we didn't use any special commands, this program runs in JSLogo as well. It doesn't work with ACSLogo as it doesn't support comments, and it needs its GUI to define procedures.
Buzz
Drawing BUZZ is basically saem as drawing FIZZ, except loops work weird way - instead of drawing starting where the turtle is, the arc degrees radius
command draws an arc around the turtle, starting where the turtle is facing and going up.
to draw_b fd 2.5 ; main stroke a bit arc 2.5 180 ; bottom loop fd 5 ; more main stroke arc 2.5 180 ; top loop fd 2.5 ; finish main stroke pu rt 180 fd 10 ; go back ; go to next character lt 90 fd 7 ; reset pen state pd lt 90endto draw_u pu fw 3 pd fd 7 ; left stroke pu rt 90 fd 6 ; move to right stroke pd rt 90 fd 7 ; right stroke pu rt 90 fd 3 ; move to center of arc pd rt 180 arc 3 180 ; arc ; go to next character pu fd 8 rt 90 fd 3 ; reset pen state pd rt 180endto draw_z rt 90 fd 5; bottom line pu rt 180 fd 5 ; return pd rt 120 fd 11 ; diagonal stroke lt 120 fd 5 ; top line ; advance to next character pu rt 180 fd 5 rt 120 fd 11 lt 120 fd 10 ; reset pen state pd lt 90endto draw_buzz draw_b draw_u draw_z draw_zendcsrepeat 3 [draw_buzz]
Digits
Doing this 10 more times with proper loops for each digit would be a bit much, so let's do them in style of 7-segment display.
For 1 I'll use the I code instead to avoid awkward spacing.
Here's the code:
; C; B D; G; A E; Fto seven_seg :a :b :c :d :e :f :g ifelse :a [pd] [pu] fd 5 ifelse :b [pd] [pu] fd 5 rt 90 ifelse :c [pd] [pu] fd 5 rt 90 ifelse :d [pd] [pu] fd 5 ifelse :e [pd] [pu] fd 5 rt 90 ifelse :f [pd] [pu] fd 5 pu rt 90 fd 5 rt 90 ifelse :g [pd] [pu] fd 5 pu fd 5 rt 90 fd 5 rt 180 pdendto draw_0 seven_seg true true true true true true falseendto draw_1 fd 10 pu rt 90 fd 5 rt 90 fd 10 pd rt 180endto draw_2 seven_seg true false true true false true trueendto draw_3 seven_seg false false true true true true trueendto draw_4 seven_seg false true false true true false trueendto draw_5 seven_seg false true true false true true trueendto draw_6 seven_seg true true true false true true trueendto draw_7 seven_seg false false true true true false falseendto draw_8 seven_seg true true true true true true trueendto draw_9 seven_seg false true true true true true trueendto draw_digits draw_0 draw_1 draw_2 draw_3 draw_4 draw_5 draw_6 draw_7 draw_8 draw_9endresetcsdraw_digitsht
And here are the digits:
As you can see procedures can take parameters, and ifelse condition [then] [else]
can do some simple logic.
Numbers
To draw numbers we just need to add a bit of code and some recursion:
...to draw_digit :digit if (:digit = 0) [draw_0] if (:digit = 1) [draw_1] if (:digit = 2) [draw_2] if (:digit = 3) [draw_3] if (:digit = 4) [draw_4] if (:digit = 5) [draw_5] if (:digit = 6) [draw_6] if (:digit = 7) [draw_7] if (:digit = 8) [draw_8] if (:digit = 9) [draw_9]endto draw_number :number make "a (:number % 10) make "b (:number - :a) make "c (:b / 10) if (:c > 0) [draw_number :c] draw_digit :aendresetcsdraw_number 42069ht
make "var (...)
is how you can assign variables. We need to use a bunch of extra variables, as Logo lacks integer division.
FizzBuzz
And here's the moment we've all been waiting for, the FizzBuzz in Logo!
Here's the complete program, mostly the code we wrote before:
; C; B D; G; A E; Fto seven_seg :a :b :c :d :e :f :g ifelse :a [pd] [pu] fd 5 ifelse :b [pd] [pu] fd 5 rt 90 ifelse :c [pd] [pu] fd 5 rt 90 ifelse :d [pd] [pu] fd 5 ifelse :e [pd] [pu] fd 5 rt 90 ifelse :f [pd] [pu] fd 5 pu rt 90 fd 5 rt 90 ifelse :g [pd] [pu] fd 5 pu fd 5 rt 90 fd 5 rt 180 pdendto draw_0 seven_seg true true true true true true falseendto draw_1 fd 10 pu rt 90 fd 5 rt 90 fd 10 pd rt 180endto draw_2 seven_seg true false true true false true trueendto draw_3 seven_seg false false true true true true trueendto draw_4 seven_seg false true false true true false trueendto draw_5 seven_seg false true true false true true trueendto draw_6 seven_seg true true true false true true trueendto draw_7 seven_seg false false true true true false falseendto draw_8 seven_seg true true true true true true trueendto draw_9 seven_seg false true true true true true trueendto draw_digit :digit if (:digit = 0) [draw_0] if (:digit = 1) [draw_1] if (:digit = 2) [draw_2] if (:digit = 3) [draw_3] if (:digit = 4) [draw_4] if (:digit = 5) [draw_5] if (:digit = 6) [draw_6] if (:digit = 7) [draw_7] if (:digit = 8) [draw_8] if (:digit = 9) [draw_9]endto draw_number :number make "a (:number % 10) make "b (:number - :a) make "c (:b / 10) if (:c > 0) [draw_number :c] draw_digit :aendto draw_i fd 10 ; main stroke ; go to next character pu rt 90 fd 5 rt 90 fd 10 ; reset pen state pd rt 180endto draw_f fd 10 ; main line rt 90 fd 5 ; top stroke pu rt 180 fd 5 lt 90 fd 5; move to next stroke pd lt 90 fd 5; middle stroke ; go to next character pu fd 5 rt 90 fd 5 ; reset pen state pd rt 180endto draw_b fd 2.5 ; main stroke a bit arc 2.5 180 ; bottom loop fd 5 ; more main stroke arc 2.5 180 ; top loop fd 2.5 ; finish main stroke pu rt 180 fd 10 ; go back ; go to next character lt 90 fd 7 ; reset pen state pd lt 90endto draw_u pu fw 3 pd fd 7 ; left stroke pu rt 90 fd 6 ; move to right stroke pd rt 90 fd 7 ; right stroke pu rt 90 fd 3 ; move to center of arc pd rt 180 arc 3 180 ; arc ; go to next character pu fd 8 rt 90 fd 3 ; reset pen state pd rt 180endto draw_z rt 90 fd 5; bottom line pu rt 180 fd 5 ; return pd rt 120 fd 11 ; diagonal stroke lt 120 fd 5 ; top line ; advance to next character pu rt 180 fd 5 rt 120 fd 11 lt 120 fd 10 ; reset pen state pd lt 90endto draw_fizz draw_f draw_i draw_z draw_zendto draw_buzz draw_b draw_u draw_z draw_zendto draw_fizzbuzz draw_fizz draw_buzzendto draw_line :i setxy 15 (:i * 15) ifelse (:i % 15 = 0) [draw_fizzbuzz] [ ifelse (:i % 5 = 0) [draw_buzz] [ ifelse (:i % 3 = 0) [draw_fizz] [draw_number :i] ] ]endresetcsmake "i 1repeat 30 [ draw_line :i make "i (1 + :i)]ht
Which generates the FizzBuzz we want:
We had to do a few more things:
setxy x y
moves the turtle to specific point on the screen - we use turtle position to go to the next letter, so we don't really know how far it went - it's a lot easier this waymake "i 1
- there's no for loops in Logo, so we make a weird while/for hybrid by definingi
and increasing it 30 times
That's enough Logo for now.
Should you use Logo?
No.
Turtle graphics is "easier" for children only in some evil mirror universe where children's favorite subject is trigonometry. In this universe coordinate graphics with damn graph paper is drastically more approachable. And if you absolutely need to do turtle graphics, there's packages for that in every regular language anyway. Even disregarding turtle vs coordinate graphics issue, Logo is absolutely dreadful as a programming language, and learning Logo teaches no useful skill.
As for teaching programming to total beginners, the easiest ways are either HTML+CSS then Javascript path (the junior web dev path), or spreadsheets then SQL path (the business analyst path). Or do what ambitious bootcamps do and start with Ruby or Python, with proper TDD from right away and so on. These approaches are all proven to work. Nobody teaches programming with Logo, as it would be horribly ineffective and completely ridiculous.
Code
All code examples for the series will be in this repository.
Original Link: https://dev.to/taw/100-languages-speedrun-episode-33-logo-3gb8
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To