Today: advanced double-while parsing, flex-arrow drawing example

at_word() - Need a Little More Power

We've done them like this:


  at    end
   v----v
xx @word xx

find_alpha()

I will work this one in lecture with the double-while technique. Then we'll have you try to solve a similar one. (HW5)

> find_alpha()

find_alpha(s): We'll say a word is made of 1 or more alphabetic chars. Find and return the first word in s, or None if there is none. So '%%%abc xx' returns 'abc'. Use two while loops.

find_alpha() Plan

1. Skip over the '%' to find the 'W'

2. Then find the end of the series of alpha


%%%Woot xx

find_alpha() - Loop 1

Loop 1 Test




%%%Woot xx
    # Advance start to first alpha
    start = 0
    while start < len(s) and not s[start].isalpha():
        start += 1

find_alpha() Loop 2

Loop 2 Test




%%%Woot xx

Completed Drawings

alt: alpha loop 1

alt: alpha loop 2

Code It Up And Run

> find_alpha()

Issue: What if there is no alpha?

Q: Suppose there is no alpha. What will start be after loop 1? What is the if-check for that case?

Reminder: here is the loop-1 code:

while start < len(s) and not s[start].isalpha():
   start += 1

Sketch out what start does for the no-alpha case:



  '%%%'
   0123

Solution: Detect No-Alpha

    if start == len(s):
        return None

find_alpha() Solution Code

def find_alpha(s):
    # Advance start to first alpha
    start = 0
    while start < len(s) and not s[start].isalpha():
        start += 1
    
    # There was no word
    if start == len(s):
        return None
    
    # Advance end past alpha chars
    end = start + 1
    while end < len(s) and s[end].isalpha():
        end += 1
    
    return s[start:end]

You Try One - find_num()

Here is a very similar problem on the experimental server. See if you can write the code for it. You can peek at the lecture example above if you need to.

find_num(s): We'll say that a number is a series of one or more digit chars. Find and return the substring of the first num in s, or None if there is none.

> find_num()

Aside: link to CC licensed music cc mixter. We're looking for ideas for some CC licensed music for coding in lecture to keep me and Juliette from blabbing away, cause neither of us can stand the awkward silence! Something mellow with no vocals.

find_num() Solution

def find_num(s):
    # Advance start to first digit
    start = 0
    while start < len(s) and not s[start].isdigit():
        start += 1
    
    # There was no num
    if start == len(s):
        return None
    
    # Advance end past digits
    end = start + 1
    while end < len(s) and s[end].isdigit():
        end += 1
    
    return s[start:end]

Flex Arrow Example

Download the flex-arrow.zip to work this fun little drawing example.

Drawing - Float Is Ok

x = 0
y = 0
width = 200
x2 = x + width

# this is fine
canvas.draw_line(x, y, x2, y)

# this is fine too
# draw a line 2/3 as long
# note that x3 is not an int
x3 = x + width * 0.66
canvas.draw_line(x, y, x3, y)

Draw Arrow Output

Ultimately we want to produce this output:

alt: 2 arrows

The "flex" parameter is 0..1.0: the fraction of the arrow's length used for the arrow heads. The arms of the arrow will go at a 45-degree angle away from the horizontal.

Starter Code: Left Arrow + flex

Specify flex on the command line so you can see how it works. Close the window to exit the program. You can also specify larger canvas sizes.

$ python3 flex-arrow.py -arrows 0.25
$ python3 flex-arrow.py -arrows 0.15
$ python3 flex-arrow.py -arrows 0.1 1200 600

draw_arrow() Starter Code

Look at the draw_arrow() function. It is given x,y of the left endpoint of the arrow and the horizontal length of the arrow in pixels. The "flex" number is between 0 .. 1.0, giving the head_len - the horizontal extent of the arrow head - called "h" in the diagram. Main() calls draw_arrow() twice, drawing two arrows in the window.

The code here draws the left arrow head.

def draw_arrow(canvas, x, y, length, flex):
    """
    Draw a horizontal line with arrow heads at both ends.
    It's left endpoint at x,y, extending for length pixels.
    "flex" is 0.0 .. 1.0, the fraction of length that the arrow
    heads should extend horizontally.
    """
    # Compute where the line ends, draw it
    x_right = x + length - 1
    canvas.draw_line(x, y, x_right, y)

    # Draw 2 arrowhead lines, up and down from left endpoint
    head_len = flex * length
    canvas.draw_line(x, y, x + head_len, y - head_len)  # up
    canvas.draw_line(x, y, x + head_len, y + head_len)  # down

    # Draw 2 arrowhead lines from the right endpoint
    # your code here
    pass

Exercise - Right Arrowhead

Look at the diagram below. Add the code to draw the head on the right endpoint of the arrow. The head_len variable "h" in the drawing. This is a solid, CS106A applied-math exercise.

alt: work out arrow-head math

draw_arrow() Solution

    # Draw 2 arrowhead lines from the right endpoint
    # your code here
    pass
    canvas.draw_line(x_right, y, x_right - head_len, y - head_len)  # up
    canvas.draw_line(x_right, y, x_right - head_len, y + head_len)  # down

Observations


A Little Something Extra

Now we're going to show you something a little beyond the regular CS106A level, and it's a teeny bit mind warping.

Arrow Trick Mode

alt: negative h arrow head

-trick Mode

Run the code with -trick like this, see what you get.

$ python3 flex-arrow.py -trick 0.1 1200 600

Note: one more thing today