CS 3723
  Programming Languages  
  3.4 Dictionaries   

Reference links given below will be to the online book: Building skills in Python. In this case, see Dictionaries. These are a familiar feature in Perl and other scripting languages, called "hashes" or "associative arrays" in Perl.


3.4.1. Months of the Year: See Dictionaries. Dictionaries can be used to translate data from one form to another. For example, suppose we want to translate the digits representing a month (01 through 12) to the name of the month (January through December). In Python we can use the following dictionary:

Use of Python Dictionary: from digits to months
% python
>>> mon = {'01': 'January', '02': 'February', '03': 'March',
           '04': 'April',   '05': 'May',      '06': 'June', 
           '07': 'July',    '08': 'August',   '09': 'September', 
           '10': 'October', '11': 'November', '12': 'December'}
>>> mon['06']
'June'
# order does not matter, could also be:
>>> mon = {'02': 'February', '10': 'October',   '01': 'January', 
           '06': 'June',     '07': 'July',      '04': 'April', 
           '03': 'March',    '08': 'August',    '05': 'May', 
           '12': 'December', '09': 'September', '11': 'November'}
>>> mon['08']
'August'

Items of Interest or for study:

  • As you can see, from an input like 03, this translates into March. The notation is like an array, where the first item of a pair plays the role of index, which is used to fetch the second item, which is like the value stored in the array at a given index.

  • The first items of pairs are called keys. There must not be any duplicates among the keys. The keys can be an integer like an index in an array, or they can be a string, or anything that is not changeable ("immutable") and "hashable".
  • The second items of pairs are called items. They are also called the value off the key. They can be any Python object at all. (Sometimes pairs themselves are called items.)

  • If we wanted, we could leave off the leading 0's, or strip them off.


3.4.2. The Inverse Dictionary: We might also want to go from the months to the digits. For this we could use the obvious inverse dictionary:

Inverse Python Dictionary: from months to digits
mon_inv = {'January':'01', 'February':'02', 'March':'03', 
           'April':'04',   'May':'05',      'June':'06',      
           'July':'07',    'August':'08',   'September':'09', 
           'October':'10', 'November':'11', 'December':'12'}

But instead of writing out this inverse dictionary, we can use Python code to create it. By definition, there should be no duplicates amoung the first parts of pairs in a dictionary. Otherwise there wouldn't be a unique result of consulting the dictionary. The code below only works if there are no duplicates amonng the second elements of pairs in a dictionary.

Python Dictionary Inverting Program
mon = {'01': 'January', '02': 'February', '03': 'March',
       '04': 'April',   '05': 'May',      '06': 'June', 
       '07': 'July',    '08': 'August',   '09': 'September', 
       '10': 'October', '11': 'November', '12': 'December'}

# construct the inverse dictionary
mon_inv = {} # start with an empty dictionary
for (k, v) in mon.items(): # for digits k and month v:
    mon_inv[v] = k # add (v, k) to mon_inv

sys.stdout.write(str(mon_inv) + "\n\n")
sys.stdout.write(mon['06'] + "\n")
sys.stdout.write(mon_inv['June'] + "\n")
sys.stdout.write(mon[mon_inv['June']] + "\n")
sys.stdout.write(mon_inv[mon['06']] + "\n")
Output (first 4 lines: newlines added, blanks changed)
{'February': '02', 'October': '10',   'March': '03', 
 'August': '08',   'May': '05',       'December': '12', 
 'June': '06',     'September': '09', 'April': '04', 
 'January': '01',  'November': '11',  'July': '07'}

June
06
June
06

Items of Interest or for study:

  • Notice that pairs of the inverse are not in any particular order. (It is the order given by the hash function.)

  • The code to invert the dictionary is not complicated. It uses the dictionary method items(), which returns each ditionary pair in turn (as a tuple). We then construct the dictionary with the pairs reversed. For this code to work, we need a special kind of dictionary: one where the values of keys ( = second items in pairs) have all the properties of the keys: "immutable", "hashable", and "no duplicates".


3.4.3. Example from writing MIPS code: Here is a program that translates simple one-operand assignments for our Tiny project into MIPS assembly language. The first dictionary gives the offset corresponding to each single-character operand. This offset could be calculated with two simple formulas, but the method here is far more general and is used for illustrative purposes. Similarly, the second dictionary translates from a single-character operand to the MIPS op-code.

Translate Assigns to MIPS Output (plus exec frame)
#!/usr/bin/python
import sys
# MIPS offsets for constants and variables
d = {"0":0, "1":   8, "2":  16, "3":  24, "4":  32, 
  "5":  40, "6":  48, "7":  56, "8":  64, "9":  72, 
  "a":  80, "b":  88, "c":  96, "d": 104, "e": 112,
  "f": 120, "g": 128, "h": 136, "i": 144, "j": 152,
  "k": 160, "l": 168, "m": 176, "n": 184, "o": 192,
  "p": 200, "q": 208, "r": 216, "s": 224, "t": 232,
  "u": 240, "v": 248, "w": 256, "x": 264, "y": 272,
  "z": 280}
# MIPS opcodes for operators
op = {"+":"add", "-":"sub", "*":"mul", "/":"div"}

def instr(x):
    s = x[2]
    t = x[4]
    a = x[3]
    r = x[0]
    sys.stdout.write( "\tl.d      $f2, " +
       str(d[s]) + "($s1)\n")
    sys.stdout.write( "\tl.d      $f4, " +
       str(d[t]) + "($s1)\n")
    sys.stdout.write("\t" + str(op[a]) +
       ".d    $f6, $f2, $f4\n")
    sys.stdout.write( "\ts.d      $f6, " +
       str(d[r]) + "($s1)\n\n")
   
instr("a=8*2")
instr("b=7*a")
instr("c=b+1")
instr("d=a/c")
instr("e=3+d")
main:   addu    $s7, $ra, $zero
        la      $s1, M
        l.d      $f2, 64($s1)
        l.d      $f4, 16($s1)
        mul.d    $f6, $f2, $f4
        s.d      $f6, 80($s1)

        l.d      $f2, 56($s1)
        l.d      $f4, 80($s1)
        mul.d    $f6, $f2, $f4
        s.d      $f6, 88($s1)

        l.d      $f2, 88($s1)
        l.d      $f4, 8($s1)
        add.d    $f6, $f2, $f4
        s.d      $f6, 96($s1)

        l.d      $f2, 80($s1)
        l.d      $f4, 96($s1)
        div.d    $f6, $f2, $f4
        s.d      $f6, 104($s1)

        l.d      $f2, 24($s1)
        l.d      $f4, 104($s1)
        add.d    $f6, $f2, $f4
        s.d      $f6, 112($s1)
# Print result + newl
        li      $v0, 3
        l.d     $f12, 112($s1)
        syscall
        li      $v0, 4
        la      $a0, NewL
        syscall
# Stuff at end ################
        addu    $ra, $s7, $zero
        jr      $ra  # ret
        .data
        .align  3
M:      .double 0.,1.,2.,3.,4.
        .double 5.,6.,7.,8.,9.
        .space  208  # a to z
NewL:   .asciiz "\n"

The output of running the MIPS program is: 3.14159292035398252 ( = 355/113).

Items of Interest or for study:

  • Continuation: The definition of the dictionary d can be continued on later lines in any way at all as soon as the { appears. However, you can't start this declaration with d = as the only thing on the line but would need d = \ .


3.4.4. Inverting Dictionary with duplicate second elements: The second components have to be "hashable" for this to work. (I didn't write this code myself.)

Inverting a Dictionary: Operators in C and their precedence
# c_ops.py: precedence of ops in C
# example with no duplicate 2nd components
import sys

op = {"+":"add", "-":"sub", "*":"mul", "/":"div"}
inv_op = {}
for k, v in op.items():
    inv_op[v] = k
sys.stdout.write(str(inv_op) + "\n\n")

# Has duplicate 2nd components (prec of C ops)
prec = {",":0,
    "=":1, "+=":1, "-=":1, "*=":1, "/=":1, "%=":1,
           "&=":1, "^=":1, "|=":1, "<<=":1, ">>=":1,
    "?:":2,
    "||":3,
    "&&":4,
    "|":5,
    "^":6,
    "&":7, 
    "==":8, "!=":8,
    "<":9, "<=":9,  ">":9, ">=": 9,
    "<<":10, ">>":10,
    "+":11,  "-":11,
    "*": 12, "/": 12,  "%":12,
    "!":13, "~":13, "++":13, "--":13, "+(u)":13,
            "-(u)":13, "*(p)":13, "&(a)":13, "(type)":13, "sizeof":13,
    "(group)":14, "[]":14, "->":14,  ".":14,} 
inv_prec = {}
for k, v in prec.items():
    inv_prec[v] = inv_prec.get(v, [])
    inv_prec[v].append(k)

sys.stdout.write(str(inv_prec) + "\n")
Output (edited to group items together with the same precedence)
{'mul': '*', 'add': '+', 'div': '/', 'sub': '-'}

{ 0: [','],
  1: ['%=', '>>=', '|=', '-=', '^=', '<<=', '=', '*=', '&=', '/=', '+='],
  2: ['?:'],
  3: ['||'],
  4: ['&&'],
  5: ['|'],
  6: ['^'],
  7: ['&'],
  8: ['!=', '=='],
  9: ['>=', '<', '>', '<='],
 10: ['>>', '<<'],
 11: ['+', '-'],
 12: ['*', '%', '/'],
 13: ['+(u)', '&(a)', '-(u)', '--', '~', 'sizeof', '!', '(type)', '++', '*(p)'],
 14: ['[]', '.', '(group)', '->']}

Items of Interest or for study:

  • The code above has an initial simple dictionary with no duplicate values.

  • In the second example, the Python code makes a list out of the varous values associated with a given integer precedence.

(Revision date: 2014-07-04. Please use ISO 8601, the International Standard.)