aquiline ascension

published: 2010-12-04
author: Hans Nowak

Python vs Scheme: dictionaries

(This is a post in the Python vs Scheme series, which compares features in these two languages, written from the perspective of an experienced Pythonista studying Scheme.)

One of the more useful data types in Python is the dictionary (also known as hash table, map or associative array in other languages). A built-in type, it is the backbone of objects and namespaces, and can be used for many other tasks that involves looking up things, due to good performance and simplicity of use. Under the hood, it is implemented using hash tables.

Scheme does not have a dictionary type by default, i.e. it is not defined in the standard. However, an interface for hash tables is defined in SRFI-69, and many Scheme implementations have (their own version of) this. Chicken is no exception; it has hash tables built in.

(Until recently these functions could be used right away; in the newest versions of Chicken it must be imported with (use srfi-69), though. The code examples below assume that this module has been imported, naturally.)

As it turns out, the use of dictionaries/hashes in both languages is very similar, although it looks different on the surface. Let's take a look at some common idioms.

(In the following table, d will stand for a Python dictionary, while h indicates a Chicken hash table.)

Python Scheme
Creating a new, empty dictionary:
d = {} (define h (make-hash-table))
Adding items to a dictionary:
d["a"] = 1
d["b"] = 2
(hash-table-set! h "a" 1)
(hash-table-set! h "b" 2)
Looking up things in a dictionary:
=> 1
=> raises KeyError
(hash-table-ref h "a")
=> 1
(hash-table-ref h "z")
=> error
Looking things up, providing a default if not found:
d.get("z", default) (hash-table-ref/default h "z" default)
Check if a given key is in the dictionary:
d.has_key("a") (hash-table-exists? h "a")
Deleting a key:
del d["a"] (hash-table-delete! h "a")
Getting a list of all keys or values:
(hash-table-keys h)
(hash-table-values h)

There are many more functions that operate on a Chicken hash table; in general, it allows greater control over what is going on "behind the scenes" than a Python dictionary. For example, you can set the hash table size, or specify custom functions for computing the hash or testing for equivalence.

There are ways to loop over a hash table, like the useful hash-table-map. The result is a list; remember that hash tables are unordered, so the items in the result list will be in arbitrary order.

#;6> (define h
--->   (alist->hash-table '((a . 1) (b . 2) (c . 3))))
#;7> h
#<hash-table (3)>
#;8> (hash-table-map h
--->   (lambda (key value)
--->     (sprintf "~s: ~s" key value)))
("c: 3" "b: 2" "a: 1")

In general, the capabilities of both Python dicts and Chicken hash tables are much the same; what you can do with one can probably be done with the other as well. Python has a dictionary literal; Chicken does not, although I suppose one could be written using reader macros, if you really felt the need for it. As the code above shows, a-lists are easily converted to hash tables and as such are "almost" a dictionary literal.

blog comments powered by Disqus