The problem: The Password Machine
The coding club gives every locker a password. The password machine takes a secret word, cuts it into pieces, and glues the pieces back together in a new order. This is its ACSL program:
INPUT S A = S[:2] B = S[3:] C = S[1:3] P = B + C + A OUTPUT P, len(P)What is output when the input is
S = "ELEPHANT"?This is the Junior What Does This Program Do? question for Contest 4: a short program that works on a string, a row of characters, traced by hand with no computer. The
IFs and loops are the ones you already know from Units 3, 6 and 10. What is new are the pieces.S[3:]andS[1:3]look exactly like Python, but in ACSL they mean something different. Read them the Python way and every line after them goes wrong.This unit teaches it in three steps: how ACSL numbers the characters of a string and cuts pieces out of it, and how to write each piece in Python (Lesson 1), how programs walk along a string to count, build, reverse and test for palindromes (Lesson 2), and how programs move a piece along a string, compare letters, and shrink a string in a loop (Lesson 3). Write down your first guess now. You will check it at the end of Lesson 1.
-
Read ACSL string positions, which start at 0, with
len(S)andS[j]. -
Apply ACSL's three substring forms, translate them to Python, and predict each piece's length.
-
Trace loops that count, build, reverse and compare strings, including palindrome tests.
-
Compare letters by alphabetical order, move a piece along a string, and shrink a string with
WHILE.
-
Lesson 1 — Strings and pieces in ACSL and in Python
Division: Junior and above.
A string is a row of characters: letters, digits, spaces and signs. Each character has a position, and in ACSL, as in Python, the first position is
0.len(S)is the number of characters, andS[j]is the character at positionj.For
S = "ELEPHANT":position 0 1 2 3 4 5 6 7 character E L E P H A N T On paper, write the positions above the letters like this before you trace anything.
S = "ELEPHANT" print(len(S)) print(S[0], S[3], S[len(S) - 1])Scroll the code for more ↓
Output
8 E P Tlen(S)is8, but the last position is7, one less, because the positions start at0.S[len(S) - 1]is always the last character, whatever the length.S[8]would be past the end: there is no position 8.Three ways to cut a piece
A piece of a string (a substring) is written with a colon inside the square brackets. ACSL has exactly three forms, and only the first one means the same as in Python.
ACSL means for "ELEPHANT"Python S[:n]the first ncharactersS[:2]is"EL"S[:n]S[n:]the last ncharactersS[3:]is"ANT"S[len(S) - n:]S[a:b]positions athroughb, both includedS[1:3]is"LEP"S[a:b + 1]Here is the table as Python.
S = "ELEPHANT" print(S[:2]) print(S[len(S) - 3:]) print(S[1:3 + 1])Scroll the code for more ↓
Output
EL ANT LEPThe three pieces are
EL,ANTandLEP. InS[n:]the number is a count from the right-hand end, not a position. InS[a:b]both ends are positions, and the piece includes both of them.The trap: reading ACSL with Python's eyes
You already know Python's slices, and that is exactly the danger. Python's
S[3:]starts at position 3 and runs to the end. Python'sS[1:3]starts at position 1 and stops before position 3.S = "ELEPHANT" print(S[3:]) print(S[1:3])Scroll the code for more ↓
Output
PHANT LEPython gives
PHANTandLE, while ACSL'sS[3:]isANTand ACSL'sS[1:3]isLEP. When a question shows ACSL pseudocode, use ACSL's rules; when it shows Python, use Python's rules. Before you trace, look at which language the program is in.How long is each piece?
Counting the characters of a piece is a quick check on your work.
ACSL number of characters S[:n]nS[n:]nS[a:b]b - a + 1So
S[j:j]is one character, the same asS[j], andS[j:j + 1]is two characters. Two pieces come up again and again:-
S[:len(S) - 1]is every character except the last one. -
S[len(S) - 1:]is the lastlen(S) - 1characters: every character except the first one.
-
T = "PENCIL" print(T[2:2 + 1], len(T[1:4 + 1])) print(T[:len(T) - 1]) print(T[len(T) - (len(T) - 1):])Scroll the code for more ↓
Output
N 4 PENCI ENCILACSL's
T[2:2]isN, a single character, and ACSL'sT[1:4]has4 - 1 + 1, which is4, characters. ACSL'sT[len(T) - 1:]asks for the last 5 characters ofPENCIL, which isENCIL: the first letter has gone. In Python that isT[1:], which the translationT[len(T) - (len(T) - 1):]works out to.Joining and comparing
+joins two strings end to end. It adds no space:"ANT" + "EATER"is"ANTEATER".==is true only when two strings are exactly the same: the same length, and the same character at every position. A space counts as a character, and so does its absence.print("ANT" + "EATER") print("CAT" == "CA" + "T") print("CAT" == "CAT ") print(len("ICE" + " " + "CREAM"))Scroll the code for more ↓
Output
ANTEATER True False 9"CAT ", with a space on the end, is not equal to"CAT", because it is one character longer."ICE" + " " + "CREAM"has3 + 1 + 5, which is9, characters, because the space you joined in counts too.When an ACSL program outputs several values, as in
OUTPUT P, len(P), this book writes them on one line separated by one space, just as Python'sprint(P, len(P))does. The strings are written without quotation marks.Checking the opening problem
Here is the password machine, translated line by line.
S[3:]becomesS[len(S) - 3:]andS[1:3]becomesS[1:4].S = "ELEPHANT" A = S[:2] B = S[len(S) - 3:] C = S[1:4] P = B + C + A print(P, len(P))Scroll the code for more ↓
Output
ANTLEPEL 8AisEL,BisANTandCisLEP, so the password isANTLEPEL, with 8 characters. Reading it with Python's eyes givesPHANT,LEand a wrong password,PHANTLEEL, with 9. Did it match your guess? Running a translation is how you check an answer while you practise; in the contest you work it out by hand, piece by piece.Put it together: this program reads a word
Sand a numbern, and makes a badge: the firstnletters joined to the lastnletters. It also outputs the middle: the letters left over between them, positionsnthroughlen(S) - 1 - n.INPUT S INPUT n badge = S[:n] + S[n:] middle = S[n:len(S) - 1 - n] OUTPUT badge, middleThe first input line holds the word, in capital letters, and the second line holds
n. The word has at least2 * n + 1letters, so the middle is never empty. It is saved asassets/l1.py.Try it S = input() n = int(input()) badge = S[:n] + S[len(S) - n:] middle = S[n:len(S) - n] print(badge, middle)Scroll the code for more ↓
Run it from this unit's folder, feeding it an input file:
python assets/l1.py < assets/l1/1.inAll three forms appear, and each one is translated differently. ACSL's
S[n:len(S) - 1 - n]ends at positionlen(S) - 1 - n, so the Python slice must stop one later, atlen(S) - n. With the wordCATandn = 1(assets/l1/2.in) the middle is the single letterA.Lesson 2 — Walking along a string
Division: Junior and above.
Many programs look at a string one character at a time. The positions run from
0tolen(S) - 1, so the loop isFOR j = 0 TO len(S) - 1 ...S[j]... NEXT jThe
- 1matters: ACSL'sFORincludes its end value, and positionlen(S)does not exist. In Python it becomesfor j in range(len(S)):, becauserangestops before its end.Counting one character
To count how often one character appears, compare each
S[j]with it and add 1 when they match.S = "EXCELLENCE" c = 0 FOR j = 0 TO len(S) - 1 IF S[j] == "E" THEN c = c + 1 END IF NEXT j OUTPUT cPython can also walk along a string without positions, with
for ch in S:. Both loops are shown here.S = "EXCELLENCE" c = 0 for j in range(len(S)): if S[j] == "E": c = c + 1 print(c) d = 0 for ch in S: if ch == "E": d = d + 1 print(d)Scroll the code for more ↓
Output
4 4Both loops count
4.for ch in Sis shorter, but it does not know where each character is. When a program uses the position too, as inS[j + 1]orS[len(S) - 1 - j], use the loop over positions.Counting a group of characters: vowels
To count several different characters, join the tests with
||(Python'sor).IF S[j] == "A" || S[j] == "E" || S[j] == "I" || S[j] == "O" || S[j] == "U" THENS = "EDUCATION" v = 0 for j in range(len(S)): ch = S[j] if ch == "A" or ch == "E" or ch == "I" or ch == "O" or ch == "U": v = v + 1 print(v, len(S) - v)Scroll the code for more ↓
Output
5 4EDUCATIONhas5vowels and4other letters. Each test must namechagain:ch == "A" or "E"does not test forE. In a trace, tick each vowel as you pass it, then count the ticks.Building a new string
A program can build a new string, starting from the empty string
""and joining one character on each pass.S = "BASKETBALL" T = "" FOR j = 0 TO len(S) - 1 IF j % 2 == 0 THEN T = T + S[j] END IF NEXT j OUTPUT TS = "BASKETBALL" T = "" for j in range(len(S)): if j % 2 == 0: T = T + S[j] print(T)Scroll the code for more ↓
Output
BSEBLj % 2 == 0keeps the positions 0, 2, 4, 6 and 8, which holdB,S,E,BandL: the answer isBSEBL. It is easy to count letters from 1 by mistake and keepA,K,T,A,Linstead. Write the positions above the word first.Reversing: which side do you join on?
T = T + S[j]joins each character on the end, so it copies the string in order.R = S[j] + Rjoins each character on the front, so the first character ends up last: the string comes out reversed.A loop that runs backwards and joins on the end reverses too:
FOR j = len(S) - 1 TO 0 STEP -1 R = R + S[j] NEXT jS = "DRAWER" T = "" R = "" for j in range(len(S)): T = T + S[j] R = S[j] + R print(T, R) B = "" for j in range(len(S) - 1, -1, -1): B = B + S[j] print(B)Scroll the code for more ↓
Output
DRAWER REWARD REWARDTisDRAWERagain, andRisREWARD. The backwards loop also givesREWARD: in Python its end is-1, one past0, so that position0is included. In a trace, write a table with a column forj, one forS[j]and one for the string after the pass:jS[j]Rafter the pass0 D D1 R RD2 A ARD3 W WARD4 E EWARD5 R REWARDPalindromes
A palindrome reads the same forwards and backwards, like
RADARorNOON. A program can test it in two ways:-
build the reverse and compare it with
==; -
compare each character with its mirror,
S[j]withS[len(S) - 1 - j], from the outside in.
For the mirror test, the loop only needs to reach the middle:
FOR j = 0 TO int(len(S) / 2) - 1.-
S = "RADAR" R = "" for j in range(len(S)): R = S[j] + R print(R == S) W = "ROTOR BLADE" same = 0 for j in range(len(W) // 2): if W[j] == W[len(W) - 1 - j]: same = same + 1 print(len(W) // 2, same)Scroll the code for more ↓
Output
True 5 0RADARreversed isRADAR, so the first test printsTrue.ROTOR BLADEhas 11 characters, so the mirror loop checks 5 pairs:R E,O D,T A,O LandR B. None of them match, sosameis0: the test found that it is not a palindrome. With an odd length, the middle character is its own mirror and is never checked, which is fine.Put it together: this program reads a word, reverses it, and says whether it is a palindrome.
INPUT S R = "" FOR j = 0 TO len(S) - 1 R = S[j] + R NEXT j IF R == S THEN OUTPUT R, "YES" ELSE OUTPUT R, "NO" END IFThe input is one word in capital letters. It is saved as
assets/l2.py.Try it S = input() R = "" for j in range(len(S)): R = S[j] + R if R == S: print(R, "YES") else: print(R, "NO")Scroll the code for more ↓
Trace it by hand on each input before you run it from this unit's folder:
python assets/l2.py < assets/l2/1.inA one-letter word (
assets/l2/3.in) is always a palindrome.ABCA(assets/l2/5.in) starts and ends with the same letter but is not a palindrome: every pair of mirrors must match, not only the outside pair.Lesson 3 — Moving pieces, comparing letters, shrinking strings
Division: Junior and above.
The hardest string questions put a piece inside a loop, compare letters, or chop a string shorter on every pass. Each one is traced the same way: work out the piece first, write it down, and only then compare or join it.
A piece that moves along the string
S[j:j + 1]is positionsjthroughj + 1: a piece of two characters that moves along asjgrows. The loop must stop atlen(S) - 2, so thatj + 1is still inside the string.S = "ASSESSMENT" c = 0 FOR j = 0 TO len(S) - 2 IF S[j:j + 1] == "SS" THEN c = c + 1 END IF NEXT j OUTPUT cIn Python the same piece is
S[j:j + 2].S = "ASSESSMENT" c = 0 for j in range(len(S) - 1): if S[j:j + 2] == "SS": c = c + 1 print(c) print(S[3:3 + 2], S[3:4])Scroll the code for more ↓
Output
2 ES ESSsits at positions 1 and 4, socis2. Atj = 3, ACSL'sS[3:4]is the two lettersES, but Python'sS[3:4]is onlyE. A reader who uses Python's rule never finds a two-letter match and answers0.Endings: the last k letters
S[k:]is the lastkcharacters, so a loop overklooks at longer and longer endings of the word.FOR k = 1 TO len(S) ...S[k:]... NEXT kIn Python the ending is
S[len(S) - k:].S = "TIGER" for k in range(1, len(S) + 1): print(k, S[len(S) - k:], S[:k])Scroll the code for more ↓
Output
1 R T 2 ER TI 3 GER TIG 4 IGER TIGE 5 TIGER TIGERThe endings are
R,ER,GER,IGERandTIGER, and the beginningsS[:k]grow from the other side. A program that comparesS[:k]withS[k:]is asking whether the word starts and ends with the samekletters.Comparing letters
<and>compare two capital letters by their order in the alphabet:"A" < "B"is true, and"M" > "Z"is false. Every string in this unit that is compared this way uses capital letters only. (In Python every capital letter comes before every small letter, so"Z" < "a"is true: never mix the two when you compare.)Comparing letters lets a program find the letter latest in the alphabet, just as it finds the largest number.
S = "SQUIRREL" big = S[0] where = 0 early = 0 for j in range(len(S)): if S[j] > big: big = S[j] where = j if S[j] < "M": early = early + 1 print(big, where, early)Scroll the code for more ↓
Output
U 2 3U, at position 2, is the latest letter, so the answer startsU 2. The letters beforeMin the alphabet areI,EandL, soearlyis3. With>, a later copy of the same letter never replaces the first one; with>=it would.Putting two letters in order
Compare two letters and join them smaller first:
a = S[j] b = S[j + 1] IF a > b THEN T = T + b + a ELSE T = T + a + b END IFS = "TOPCAT" T = "" for j in range(0, len(S), 2): a = S[j] b = S[j + 1] if a > b: T = T + b + a else: T = T + a + b print(T)Scroll the code for more ↓
Output
OTCPATThe pairs are
TO,PCandAT.Tcomes afterO, so the first pair becomesOT;PCbecomesCP;ATis already in order. The result isOTCPAT.Shrinking a string with WHILE
A
WHILEloop can chop a string shorter on every pass until it is used up.-
S = S[len(S) - 1:]keeps the lastlen(S) - 1characters: it drops the first character. -
S = S[:len(S) - 1]keeps the firstlen(S) - 1characters: it drops the last character.
Remember to compute
len(S)for the string as it is now, since it shrinks on every pass.-
S = "PLANET" while len(S) > 0: print(S) S = S[len(S) - (len(S) - 1):]Scroll the code for more ↓
Output
PLANET LANET ANET NET ET TThe lines are
PLANET,LANET,ANET,NET,ETandT. On the pass that printsT,len(S) - 1is0, so the nextSis the last 0 characters: the empty string, and the loop stops. The Python pieceS[len(S) - (len(S) - 1):]is the translation rule applied as it is; it works out toS[1:].Put it together: this program reads a word
Sand a shorter patternP. It counts how many timesPappears inS, overlaps included, and outputs the count and the first position wherePstarts (or-1if it never does).INPUT S INPUT P m = len(P) c = 0 first = -1 FOR j = 0 TO len(S) - m IF S[j:j + m - 1] == P THEN c = c + 1 IF first == -1 THEN first = j END IF END IF NEXT j OUTPUT c, firstThe first input line holds the word and the second holds the pattern, both in capital letters. It is saved as
assets/l3.py.Try it S = input() P = input() m = len(P) c = 0 first = -1 for j in range(len(S) - m + 1): if S[j:j + m] == P: c = c + 1 if first == -1: first = j print(c, first)Scroll the code for more ↓
Run it from this unit's folder:
python assets/l3.py < assets/l3/1.inThe ACSL piece
S[j:j + m - 1]has(j + m - 1) - j + 1, which ism, characters: exactly the length of the pattern. InBANANAthe patternANAis found at positions 1 and 3, which overlap (assets/l3/1.in). When the pattern is longer than the word (assets/l3/5.in), the loopFOR j = 0 TO -1runs zero times, so the output is0 -1.-
In ACSL,
S[:n]is the first n characters,S[n:]the last n, andS[a:b]positions a through b. -
In Python those are
S[:n],S[len(S) - n:]andS[a:b + 1]. -
Joining each character on the end copies a string; joining on the front reverses it.
-
A palindrome reads the same forwards and backwards.
-
Read ACSL with ACSL's rules: the Password Machine prints
ANTLEPEL 8, notPHANTLEEL 9.
You can now trace an ACSL program that works on strings and write its exact output.
-