Welcome, guest | Sign In | My Account | Store | Cart

In a little Prolog interpreter I'm writing I needed a simple and concise way to create Var-objects and also store them in a mapping varname -> Var-object representing the scope of the current Prolog rule. Also, anonymous Prolog variables ("_") should not go into the mapping and should be renamed to _<unique-number>. I came up with this:

Python, 48 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from collections import defaultdict
from itertools import count


class Var(object):

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return "Var(%s)" % self.name


class vardict(defaultdict):

    def __init__(self, *args, **kwargs):
        super(vardict, self).__init__(Var, *args, **kwargs)

    def __missing__(self, key, unique=count()):
        if self.default_factory is None:
            raise KeyError(key)
        if key == "_":
            return self.default_factory(key + str(next(unique)))
        self[key] = value = self.default_factory(key)
        return value


if __name__ == "__main__":

    vdict = vardict()
    vlist = []

    vlist.append(vdict["First"])
    vlist.append(vdict["Second"])
    vlist.append(vdict["_"])
    vlist.append(vdict["First"])
    vlist.append(vdict["Second"])
    vlist.append(vdict["_"])

    vlist.sort()

    print
    for key, value in vdict.items():
        print key, ":", value

    print
    for each in vlist:
        print id(each), ":", each

vardict follows the definition of defaultdict quite closely, except:

1) it's __init__-method doesn't take a default_factory argument,

2) "_" is not inserted into the dict and also renamed to _<unique-number>,

3) __missing__'s key parameter is passed on to the default_factory.

Point 2) ensures that every anoymous variable gets renamed and instantiated uniquely, whereas other variables are managed as singletons in the current Prolog rule's namespace.

Point 3) shows the main motivation for this recipe. I wanted to add newly created Var-objects into a dictionary and defaultdict seemed almost useful for this task, except Var-objects needed to know their own name, and that was not possible as-is. But since default_factory is only called from within __missing__, nothing inhibits us from changing it to accept an argument if __missing__ is changed as well.