%%HP: T(3)A(D)F(.);
@
@ This directory contains two programs for managing an association list
@ or dictionary datastructure. For those of you who don't know what that
@ is: this is a datastructure in which you can store pairs of the form
@ (key,value). You can look up what value is associated with which key.
@ The key and the value can be any object you want.
@
@ The programs are:
@
@ Insert:       ASSOC Object Object -> ASSOC
@
@ This takes a dictionary, a key and a value. If the key does not exist
@ already, the pair (key,value) is just added to the dictionary). If the
@ key existed already in the dictionary, then it's previous value is
@ replaced by the new value. A key will therefore alway be present only
@ once in every dictionary. The new dictionary is returned to the stack.
@
@ Lookup:       ASSOC Object -> Object
@               ASSOC Object -> NotFound
@
@ This takes a dictionary and a key. If the key is present in the
@ dictionary, the associated value is returned. If the key is not present
@ than the value of the variable NotFound is returned (or the name
@ 'NotFound' if NotFound does not have a value).
@
@ A dictionary is stored as a two element list: the first element is a
@ list of keys and the second element is a list of values (in the same
@ order as the keys). An empty dictionary would be {{}{}}. This value is
@ supplied as Empty in the directory below.
@
@ Personally I find the use of NotFound very handy. If you only store
@ positive numbers in your dictionary and you need to add them, you can
@ make NotFound equal to 0 (as in the directory below) and just forget
@ to check if the key existed or not. The same goes for the case where
@ you keep lists of things in your dictionary. Simply make NotFound the
@ empty list ({}) and if you want to add an element, you simply Lookup
@ the key, add the new element to the result and Insert that back in the
@ original dictionary. If you want to check yourself, simply PURGE the
@ variable NotFound and if a key is not in a dictionary when you look it
@ up, you can check if the result is 'NotFound' yourself. Talk about
@ flexible!
@
@ As you can see, the most part of the programs is just parameter checking.
@ The actual insertion and looking up is done with very little code. You may
@ use these programs for whatever you wish (I would like to be credited
@ for making them if you do, that's all...), so if you know that the
@ parameters you'll be giving to Insert and Lookup are the correct types
@ you can just throw those tests out and get better performance.
@
@ Jarno Peschier, http://www.students.cs.ruu.nl/people/jpeschie
@
DIR
  Insert                        @ Insert a (key,value) into the dictionary
    \<<
      IF DEPTH 3 <              @ Check number of parameters
      THEN # 201h
DOERR
      END \-> key val           @ Get key and value
      \<<
        IF DUP TYPE             @ If dictionary is a name...
6 ==
        THEN
          IF DUP                @ ...and it contains a list...
VTYPE 5 ==
          THEN RCL              @ ...then: recall it to stack...
          ELSE key              @ ...else: error
val # 202h DOERR
          END
        END
        IF DUP TYPE             @ If dictionary no list...
5 \=/
        THEN key                @ ...then: error
val # 202h DOERR
        END
        IF DUP SIZE             @ If dictionary is not a 2 element list...
2 \=/
        THEN key                @ ...then: error
val # 203h DOERR
        END OBJ\->
DROP
        IF OVER                 @ If both parts are not a list themselves...
TYPE 5 \=/ OVER TYPE
5 \=/ AND
        THEN 2                  @ ...then: error
\->LIST key val
# 203h DOERR
        END
        IF OVER                 @ If keylist longer than valuelist...
SIZE OVER SIZE >
        THEN 2                  @ ...then: error
\->LIST key val
# 203h DOERR
        END \-> keys            @ Get both lists
vals
        \<<
          IF keys               @ If key is not yet present in dictionary...
key POS DUP 0 ==
          THEN DROP             @ ...then: simply add it at the back...
keys key + vals val
            IF DUP              @ (If it's a list, make sure it gets
TYPE 5 ==                       @ put in is it should!)
            THEN 1
\->LIST
            END +
          ELSE keys             @ ...else: replace old value at the key
vals ROT val PUT
          END 2                 @ Return the 2 element dictionary list
\->LIST
        \>>
      \>>
    \>>
  Lookup                        @ Lookup the value of a key in the dictionary
    \<<
      IF DEPTH 2 <              @ Check number of parameters
      THEN # 201h
DOERR
      END \-> key               @ Get key
      \<<
        IF DUP TYPE             @ If dictionary is a name...
6 ==
        THEN
          IF DUP                @ ...and it contains a list...
VTYPE 5 ==
          THEN RCL              @ ...then: recall it to stack...
          ELSE key              @ ...else: error
# 202h DOERR
          END
        END
        IF DUP TYPE             @ If dictionary no list...
5 \=/
        THEN key                @ ...then: error
# 202h DOERR
        END
        IF DUP SIZE             @ If dictionary is not a 2 element list...
2 \=/
        THEN key                @ ...then: error
# 203h DOERR
        END OBJ\->
DROP
        IF OVER                 @ If both parts are not a list themselves...
TYPE 5 \=/ OVER TYPE
5 \=/ AND
        THEN 2                  @ ...then: error
\->LIST key # 203h
DOERR
        END
        IF OVER                 @ If keylist longer than valuelist...
SIZE OVER SIZE >
        THEN 2                  @ ...then: error
\->LIST key # 203h
DOERR
        END \-> keys            @ Get both lists
vals
        \<<
          IF keys               @ If key is present in dictionary...
key POS DUP 0 \=/
          THEN vals             @ ...then: return it's value...
SWAP GET
          ELSE DROP             @ ...else: return value of NotFound
NotFound
          END
        \>>
      \>>
    \>>
  Empty { { } { } }             @ An empty dictionary looks like this
  NotFound 0                    @ Default NotFound value: 0
END
