Hybrid assoc lists
This commit is contained in:
parent
b0a49fb71d
commit
b6ae110f66
40
docs/Hybrid-assoc-lists.md
Normal file
40
docs/Hybrid-assoc-lists.md
Normal file
|
@ -0,0 +1,40 @@
|
|||
# Hybrid assoc lists
|
||||
|
||||
In it's current very prototype stage, PSSE has to forms of name/value store. One is the assoc list, the other is the hashmap.
|
||||
|
||||
An assoc (association) list is a list of the form:
|
||||
|
||||
((name<sub>1</sub> . value<sub>1</sub>)(name<sub>2</sub> . value<sub>2</sub>)(name<sub>3</sub> . value<sub>3</sub>)...)
|
||||
|
||||
Hashmaps have many very clear advantages, but assoc lists have one which is very important in the evaluation environment, and that is precisely its sequentiality. Thus, if the same name is bound twice on an assoc list, the value nearest the head is the one which will be recovered:
|
||||
|
||||
(assoc :bar '((:foo . 1) (:bar . "Hello there!")(:ban . 3)(:bar . 2)))
|
||||
=> "Hello there!"
|
||||
|
||||
Why does this matter? Well, for precisely the same reason it matters when a UNIX system searches for an executable.
|
||||
|
||||
Suppose Clare is a user who trusts both Alice and Bob, but she trusts Alice more than Bob. Suppose both Alice and Bob have written implementations of a function called `froboz`. Suppose Clare invokes
|
||||
|
||||
(froboz 3)
|
||||
|
||||
Which implementation of `froboz` should be evaluated? An assoc list makes that simple. If Clare binds Alice's implementation into her environment later than Bob's, Alice's will be the one found.
|
||||
|
||||
But an assoc list is also fearsomely inefficient, especially if we are in a system with many thousands of names, each of which may be bound multiple times in typical runtime environment.
|
||||
|
||||
How to resolve this? How to get some of the benefits of sequential access of assoc lists, with some of the efficiency benefits of hashmaps? What I'm going to propose is a **hybrid assoc list**, that is to say, a list whose members are either
|
||||
|
||||
1. (key . value) pairs, or else
|
||||
2. hashmaps.
|
||||
|
||||
So suppose we have a list, `l`, thus:
|
||||
|
||||
((:foo . 1) (:bar . 2) {:foo "not this" :ban 3} (:ban . "not this either") (:froboz . 4))
|
||||
|
||||
Then:
|
||||
|
||||
(assoc :foo l) => 1
|
||||
(assoc :bar l) => 2
|
||||
(assoc :ban l) => 3
|
||||
(assoc :froboz l) => 4
|
||||
|
||||
This will make the implementation of namespaces and search paths vastly easier.
|
|
@ -82,6 +82,7 @@ bool equal( struct cons_pointer a, struct cons_pointer b ) {
|
|||
* structures can be of indefinite extent. It *must* be done by
|
||||
* iteration (and even that is problematic) */
|
||||
result =
|
||||
cell_a->payload.string.hash == cell_b->payload.string.hash &&
|
||||
cell_a->payload.string.character ==
|
||||
cell_b->payload.string.character &&
|
||||
( equal( cell_a->payload.string.cdr,
|
||||
|
|
|
@ -90,56 +90,67 @@ internedp( struct cons_pointer key, struct cons_pointer store ) {
|
|||
*/
|
||||
struct cons_pointer c_assoc( struct cons_pointer key,
|
||||
struct cons_pointer store ) {
|
||||
struct cons_pointer result = NIL;
|
||||
struct cons_pointer result = NIL;
|
||||
|
||||
debug_print( L"c_assoc; key is `", DEBUG_BIND );
|
||||
debug_print_object( key, DEBUG_BIND );
|
||||
debug_print( L"`\n", DEBUG_BIND );
|
||||
debug_print( L"c_assoc; key is `", DEBUG_BIND );
|
||||
debug_print_object( key, DEBUG_BIND );
|
||||
debug_print( L"`\n", DEBUG_BIND );
|
||||
|
||||
if ( consp( store ) ) {
|
||||
for ( struct cons_pointer next = store;
|
||||
consp( next ); next = pointer2cell( next ).payload.cons.cdr ) {
|
||||
struct cons_space_object entry =
|
||||
pointer2cell( pointer2cell( next ).payload.cons.car );
|
||||
if ( consp( store ) ) {
|
||||
for ( struct cons_pointer next = store;
|
||||
nilp( result ) && ( consp( next ) || hashmapp( next ) );
|
||||
next = pointer2cell( next ).payload.cons.cdr ) {
|
||||
if ( consp( next ) ) {
|
||||
struct cons_pointer entry_ptr = c_car( next );
|
||||
struct cons_space_object entry = pointer2cell( entry_ptr );
|
||||
|
||||
switch ( entry.tag.value ) {
|
||||
case CONSTV:
|
||||
if ( equal( key, entry.payload.cons.car ) ) {
|
||||
result = entry.payload.cons.cdr;
|
||||
break;
|
||||
result = entry.payload.cons.cdr;
|
||||
}
|
||||
break;
|
||||
case VECTORPOINTTV:
|
||||
result = hashmap_get( entry_ptr, key );
|
||||
break;
|
||||
default:
|
||||
throw_exception(
|
||||
c_string_to_lisp_string( L"Store entry is of unknown type" ),
|
||||
NIL );
|
||||
}
|
||||
} else if ( hashmapp( store ) ) {
|
||||
result = hashmap_get( store, key );
|
||||
} else {
|
||||
result =
|
||||
throw_exception( c_string_to_lisp_string
|
||||
( L"Store is of unknown type" ), NIL );
|
||||
}
|
||||
}
|
||||
} else if ( hashmapp( store ) ) {
|
||||
result = hashmap_get( store, key );
|
||||
} else if (!nilp(store)) {
|
||||
result = throw_exception(
|
||||
c_string_to_lisp_string( L"Store is of unknown type" ), NIL );
|
||||
}
|
||||
|
||||
debug_print( L"c_assoc returning ", DEBUG_BIND );
|
||||
debug_print_object( result, DEBUG_BIND );
|
||||
debug_println( DEBUG_BIND );
|
||||
debug_print( L"c_assoc returning ", DEBUG_BIND );
|
||||
debug_print_object( result, DEBUG_BIND );
|
||||
debug_println( DEBUG_BIND );
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new key/value store containing all the key/value pairs in this store
|
||||
* with this key/value pair added to the front.
|
||||
*/
|
||||
struct cons_pointer
|
||||
set( struct cons_pointer key, struct cons_pointer value,
|
||||
struct cons_pointer store ) {
|
||||
struct cons_pointer result = NIL;
|
||||
/**
|
||||
* Return a new key/value store containing all the key/value pairs in this
|
||||
* store with this key/value pair added to the front.
|
||||
*/
|
||||
struct cons_pointer set( struct cons_pointer key, struct cons_pointer value,
|
||||
struct cons_pointer store ) {
|
||||
struct cons_pointer result = NIL;
|
||||
|
||||
debug_print( L"set: binding `", DEBUG_BIND );
|
||||
debug_print_object( key, DEBUG_BIND );
|
||||
debug_print( L"` to `", DEBUG_BIND );
|
||||
debug_print_object( value, DEBUG_BIND );
|
||||
debug_print( L"` in store ", DEBUG_BIND );
|
||||
debug_dump_object( store, DEBUG_BIND );
|
||||
debug_println( DEBUG_BIND );
|
||||
debug_print( L"set: binding `", DEBUG_BIND );
|
||||
debug_print_object( key, DEBUG_BIND );
|
||||
debug_print( L"` to `", DEBUG_BIND );
|
||||
debug_print_object( value, DEBUG_BIND );
|
||||
debug_print( L"` in store ", DEBUG_BIND );
|
||||
debug_dump_object( store, DEBUG_BIND );
|
||||
debug_println( DEBUG_BIND );
|
||||
|
||||
if ( nilp( store ) || consp( store ) ) {
|
||||
if ( nilp( store ) || consp( store ) ) {
|
||||
result = make_cons( make_cons( key, value ), store );
|
||||
} else if ( hashmapp( store ) ) {
|
||||
result = hashmap_put( store, key, value );
|
||||
|
|
Loading…
Reference in a new issue