Strings

s = "abc"
len(s)  #evaluates to 3

Square brackets are used to perform indexing into a string to get the value at a certain index or position.

s = "abc"
#index: 0 1 2 ... : indexing always starts at zero
#index: -1 -2 ... : last element always at -1
#s[0] evaluates to "a"
#s[1] evaluates to "b"
#s[2] evaluates to "c"
#s[3] trying to index out of bounds, error
#s[-1] evaluates to "c"
#s[-2] evaluates to "b"
#s[-3] evaluates to "a"

Can slice strings using [start:stop:step]. If give two numbers, [start:stop], step=1 by default. You can also omit numbers and leave just colons (to use default values).

s = "abcdefgh"
s[3:6] evaluates to "def", same as s[3:6:1]
s[3:6:2] evaluates to "df"
s[::] evaluates to "abcdefgh", same as s[0:len(s):1]
s[::-1] evaluates to "hgfedcba", same as s[-1:-(len(s)+1):-1]
s[4:1:-2] evaluates to "ec"

strings are ``immutable'' --- cannot be modified

s = "hello"
s[0] = 'y'   # gives an error
s = 'y' + s[1:len(s)]   #ok, s is bound to a new object
Earlier, we had s -> "hello", now we have s -> "yello".

for loops have a loop variable that iterates over a set of values

for var in range(4):  # var iterates over values 0,1,2,3
  <expressions>   # expressions inside loop execute with each value for var
for var in range(4,6):  # var iterates over values 4,5
  <expressions>
range is a way to iterate over numbes, but a for loop variable can iterate over any set of values, not just numbers!

The following two code snippets do the same thing:

s = "abcdefgh"
for index in range(len(s)):
  if s[index] == 'i' or s[index] == 'u':
    print("There is an i or u")

for char in s:
  if char == 'i' or char == 'u':
    print("There is an i or u")
The latter is more succinct and easier to read.

Example:

an_letters = "aefhilmnorsxAEFHILMNORSX"

word = input("Enter a word: ")
times = int(input("Enthusiasm level (1-10): "))

i = 0
while i < len(word):
  char = word[i]
  if char in an_letters:
    print("Give me an " + char + "! " + char)
  else:
    print("Give me a " + char + "! " + char)
  i += 1
print("What does that spell?")
for i in range(times):
  print(word, "!")

A slightly better version (due to its succinctness and readability):

an_letters = "aefhilmnorsxAEFHILMNORSX"

word = input("Enter a word: ")
times = int(input("Enthusiasm level (1-10): "))

i = 0
for char in word:
  char = word[i]
  if char in an_letters:
    print("Give me an " + char + "! " + char)
  else:
    print("Give me a " + char + "! " + char)
print("What does that spell?")
for i in range(times):
  print(word, "!")

Exercise: given two strings, check if they have equal lengths and they have a common letter.

s1 = input("Enter first word: ")
s2 = input("Enter second word: ")
if len(s1) == len(s2):
  for char1 in s1:
    for char2 in s2:
      if char1 == char2:
        print("common letter")
        break

Guess and Check

One of the most basic algorithms is called exhaustive enumeration. Given a problem:

Cube root using guess and check

cube = int(input("Enter a number: ")
for guess in range(cube+1):
  if guess**3 == cube:
    print("Cube root of", cube, "is", guess)
Do we really need to iterate all the way till cube+1?

A faster and more robust version

cube = int(input("Enter a number: ")
for guess in range(abs(cube)+1):
  if guess**3 >= abs(cube):
    break
if guess**3 != abs(cube):
  print(cube, 'is not a perfect cube')
else:
  if cube < 0:
    guess = -guess
  print('Cube root of ' + str(cube) + ' is ' + str(guess))