Associative data store
[1/41] from: joel::neely::fedex::com at: 14-Sep-2000 16:01
The proposed implementation below addresses separation of key/value
spaces and tries to take advantage of REBOL built-ins for performance
wherever feasible. Critiques and comments welcome!
-jn-
===============(begin code listing)==================
REBOL [
Title: "Minimal Associative Data Store"
File: %assoc.r
Date: 14-Sep-2000
Author: "Joel Neely"
Purpose: {
Quick, minimal implementation of a data store that
associates arbitrary values with (string!) keys.
}
]
assoc: make object! [
_keys: copy []
_vals: copy []
clear: func [] [_keys: copy [] _vals: copy []]
clear?: func [] [empty? head _keys]
count?: func [] [length? _keys]
new: func [/local r] [r: make assoc [] r/clear r]
put: func [k [string!] v [any-type!] /local p] [
either none? p: find _keys k [
append _keys k
append _vals v
][
change at _vals index? p v
]
v
]
get: func [k [string!] /local p] [
either none? p: find _keys k [
none
][
pick _vals index? p
]
]
keys: func [] [copy _keys]
]
=================(end code listing)==================
This (minimal) implementation can be used as follows:
=================(begin transcript)>> do %assoc.r
>> phonelist: assoc/new
>> phonelist/clear?
== true
>> phonelist/put "Bill" "555-1111"
== "555-1111"
>> phonelist/put "Al" "555-2222"
== "555-2222"
>> phonelist/put "George" "555-3333"
== "555-3333"
>> phonelist/put "Dick" "555-4444"
== "555-4444"
>> phonelist/get "Al"
== "555-2222"
>> phonelist/count?
== 4
>> phonelist/keys
== ["Bill" "Al" "George" "Dick"]
>> foreach who phonelist/keys [print [who "^-" phonelist/get who]]
Bill 555-1111
Al 555-2222
George 555-3333
Dick 555-4444
>> foreach who sort phonelist/keys [print [who "^-" phonelist/get who]]
Al 555-2222
Bill 555-1111
Dick 555-4444
George 555-3333
>> privatelist: phonelist/new
>> privatelist/count?
== 0
>>
>> foreach who head reverse sort phonelist/keys [
[ print [who "^-" phonelist/get who]
[ ]
George 555-3333
Dick 555-4444
Bill 555-1111
Al 555-2222
>>==================(end transcript)===================
Enjoy!
-jn-
--
; Joel Neely [joel--neely--fedex--com] 901-263-4460 38017/HKA/9677
REBOL [] print to-string debase/64 decompress #{
789C0BCE0BAB4A7176CA48CAB53448740FABF474F3720BCC
B6F4F574CFC888342AC949CE74B50500E1710C0C24000000}
[2/41] from: bobr:dprc at: 14-Sep-2000 13:07
excellent!
I have often wondered why select mixes keyspace with valuespace.
- anyone want to try a 'hash based version ?
- anyone want to make a /remove refinement ?
At 04:01 PM 9/14/00 -0500, [joel--neely--fedex--com] wrote:
>The proposed implementation below addresses separation of key/value
>spaces and tries to take advantage of REBOL built-ins for performance
>wherever feasible. Critiques and comments welcome!
>
>-jn-
>
;# mailto: [bobr--dprc--net]
[3/41] from: rebol:keithdevens at: 14-Sep-2000 14:06
I'm very impressed! This seems like it'll be really useful. Just wondering
how performance will be. :)
I still want something like this built into the language. Also, that way we
could use built ins like 'length? on it.
Hmm... this brings up another question. There's no way to do any kind of
operator overloading in Rebol, so that we *could* use 'length? on this data
structure, is there?
[4/41] from: joel:neely:fedex at: 14-Sep-2000 19:18
Silly me! Thanks for catching the oversight.
[bobr--dprc--net] wrote:
> - anyone want to make a /remove refinement ?
>
================================================================
REBOL [
Title: "Minimal Associative Data Store"
File: %assoc.r
Date: 14-Sep-2000
Author: "Joel Neely"
Purpose: {
Quick, minimal implementation of a data store that
associates arbitrary values with (string!) keys.
}
]
assoc: make object! [
_keys: copy []
_vals: copy []
clear: func [] [_keys: copy [] _vals: copy []]
clear?: func [] [empty? head _keys]
count?: func [] [length? _keys]
new: func [/local r] [r: make assoc [] r/clear r]
put: func [k [string!] v [any-type!] /local p] [
either none? p: find _keys k [
append _keys k
append _vals v
][
change at _vals index? p v
]
v
]
get: func [k [string!] /local p] [
either none? p: find _keys k [
none
][
pick _vals index? p
]
]
delete: func [k [string!] /local p v] [
either none? p: find _keys k [
none
][
k: pick _vals p: index? p
remove at _keys p
remove at _vals p
k
]
]
keys: func [] [copy _keys]
]
================================================================
--
; Joel Neely [joel--neely--fedex--com] 901-263-4460 38017/HKA/9677
REBOL [] print to-string debase/64 decompress #{
789C0BCE0BAB4A7176CA48CAB53448740FABF474F3720BCC
B6F4F574CFC888342AC949CE74B50500E1710C0C24000000}
[5/41] from: joel:neely:fedex at: 14-Sep-2000 19:32
[rebol--keithdevens--com] wrote:
> ... Just wondering how performance will be. :)
>
That was the motivation for using paired blocks rather than interleaving
the keys and values in a single block. Notice that all of the heavy
lifting is still done by find , remove , and change , which are all
primitives (and presumably heavily optimized... right RT? ;-) The other
built-in, append , is a VERY thin mezzanine wrapper onto insert , which
IS primitive. so I think performance will be tolerable. The main overhead
is function entry/exit (I suspect) which is unavoidable if we are to add
any functionality at all.
The main issue is to avoid loops in high-level code. If I had used
interleaved keys and values, get would have to be written something
like the following:
;!!! WARNING: UNTESTED CODE FRAGMENT !!!
assoc: make object! [
_kvpairs: copy []
;
; etc...
;
get: func [t [string!] / local k v] [
foreach [k v] _kvpairs [
if k = t [return v]
]
none
]
;
; etc...
;
]
which puts real work (proportional to the size of the store) up into
interpreted code. That's why I avoided it.
> I still want something like this built into the language. Also, that way we
> could use built ins like 'length? on it.
>
> Hmm... this brings up another question. There's no way to do any kind of
> operator overloading in Rebol, so that we *could* use 'length? on this data
> structure, is there?
>
Your wish is my command! (At least, this once! ;-)
================================================================
REBOL [
Title: "Minimal Associative Data Store"
File: %assoc.r
Date: 14-Sep-2000
Author: "Joel Neely"
Purpose: {
Quick, minimal implementation of a data store that
associates arbitrary values with (string!) keys.
}
]
assoc: make object! [
_keys: copy []
_vals: copy []
clear: func [] [_keys: copy [] _vals: copy []]
empty?: func [] [system/words/empty? head _keys]
length?: func [] [system/words/length? _keys]
new: func [/local r] [r: make assoc [] r/clear r]
put: func [k [string!] v [any-type!] /local p] [
either none? p: find _keys k [
append _keys k
append _vals v
][
change at _vals index? p v
]
v
]
get: func [k [string!] /local p] [
either none? p: find _keys k [
none
][
pick _vals index? p
]
]
delete: func [k [string!] /local p v] [
either none? p: find _keys k [
none
][
k: pick _vals p: index? p
remove at _keys p
remove at _vals p
k
]
]
keys: func [] [copy _keys]
]
================================================================
-jn-
--
; Joel Neely [joel--neely--fedex--com] 901-263-4460 38017/HKA/9677
REBOL [] print to-string debase/64 decompress #{
789C0BCE0BAB4A7176CA48CAB53448740FABF474F3720BCC
B6F4F574CFC888342AC949CE74B50500E1710C0C24000000}
[6/41] from: rebol:techscribe at: 14-Sep-2000 13:37
Hi Joel,
Nice.
you wrote:
>Critiques and comments welcome!
With respect to the line:
> new: func [/local r] [r: make assoc [] r/clear r]
I think it would be safer to use self instead of assoc
new: func [/local r] [r: make self [] r/clear r]
^^^^
2. An alternative:
assoc: [keys [] values []]
is-assoc?: func [assoc [block!]] [
all [equal? length? assoc 4 find assoc 'keys find assoc 'values]
]
contains-key?: func [assoc [block!] key [any-type!] ] [
all [is-assoc? assoc find assoc/keys key]
]
assoc-add: func [assoc [block!] candidates [block!] /local found] [
if is-assoc? assoc [
foreach [key value] candidates [
either found: contains-key? assoc key [
insert at assoc/values index? found value
][
insert assoc/keys key
insert assoc/values value
]
]
return true
]
return false
]
assoc-get: func [assoc [block!] key [any-type!] /local found] [
if found: contains-key? assoc key [
return pick assoc/values index? found
]
return none
]
>> phonelist: copy/deep assoc
== [keys [] values []]
>> assoc-add phonelist [Bill "555-1111"]
== true
>> assoc-add phonelist [Al "555-2222"]
== true
>> assoc-add phonelist [George "555-3333"]
== true
>> assoc-add phonelist [Dick "555-4444"]
== true
>> assoc-add phonelist [Jane "555-3322" Judy "555-2233"]
== true
>> assoc-get phonelist 'Bill
== "555-1111"
>> assoc-get phonelist 'Dick
== "555-4444"
>> assoc-get phonelist 'George
== "555-3333"
>> assoc-get phonelist 'Jane
== "555-3322"
>> assoc-get phonelist 'Judy
== "555-2233"
>> assoc-get phonelist 'Al
== "555-2222"
>> foreach who sort copy phonelist/keys [print [who assoc-get phonelist who]]
Al 555-2222
Bill 555-1111
Dick 555-4444
George 555-3333
Jane 555-3322
Judy 555-2233
Looks to me like it accomplishes pretty much the same thing
> separation of key/value spaces
without the additional overhead of an object (function code duplication for
each list). In addition I see the advantage of being able to effortlessly
add multiple items by including them in a block (batch adding ;-).
At 04:01 PM 9/14/00 -0500, you wrote:
>The proposed implementation below addresses separation of key/value
>spaces and tries to take advantage of REBOL built-ins for performance
<<quoted lines omitted: 84>>
> 789C0BCE0BAB4A7176CA48CAB53448740FABF474F3720BCC
> B6F4F574CFC888342AC949CE74B50500E1710C0C24000000}
;- Elan [ : - ) ]
author of REBOL: THE OFFICIAL GUIDE
REBOL Press: The Official Source for REBOL Books
http://www.REBOLpress.com
visit me at http://www.TechScribe.com
[7/41] from: rsnell:webtrends at: 14-Sep-2000 14:10
Great stuff from both of you - I am thinking of changing my
INI file handling code to one of these methods (where I have
a hash of hashes so that a section value is really itself an
associative array).
But Elan, what do you mean when you say that there is a
problem with function code duplication using the object approach?
I haven't used a lot of Rebol objects yet, but my OOP experience
tells me (yes I know I'm supposed to forget it about that!) that
there is only one instance of code for all objects based on a
base template.
TIA,
Rodney
[8/41] from: rebol:techscribe at: 14-Sep-2000 14:34
Hi Joel,
you wrote:
>[rebol--keithdevens--com] wrote:
>>
<<quoted lines omitted: 17>>
> none
> ]
If your only concern is avoiding loops, and that is why you are not using a
single block to contain both keys and values, then why not use a single
block in combination with the path notation?
assoc: make object! [
_kvpairs: copy []
get: func [t [string!] /local path] [
path: make path! compose [_kvpairs (t)]
return path
]
That should do it. You can do the whole thing with paths, without supplying
any new functions or objects:
>> phonelist: make block! 10
== []
>> insert phonelist [Jim "555-3333"]
== []
>> insert phonelist [Dick "555-2222"]
== [Jim "555-3333"]
>> insert phonelist [Judy "555-4444" Jane "555-5555"]
== [Dick "555-2222" Jim "555-3333"]
>> insert phonelist [Bill "555-1111"]
== [Judy "555-4444" Jane "555-5555" Dick "555-2222" Jim "555-3333"]
>> insert phonelist [Al "555-9999"]
== [Bill "555-1111" Judy "555-4444" Jane "555-5555" Dick "555-2222" Jim
555-3333
]
>> phonelist/Bill
== "555-1111"
>> phonelist/Dick
== "555-2222"
>> phonelist/Jim
== "555-3333"
>> phonelist/Judy
== "555-4444"
>> phonelist/Al
== "555-9999"
>> phonelist/Jane
== "555-5555"
>> foreach [who phone] sort/skip copy phonelist 2 [
[ print [who phone]
[ ]
Al 555-9999
Bill 555-1111
Dick 555-2222
Jane 555-5555
Jim 555-3333
Judy 555-4444
>;
>; etc...
<<quoted lines omitted: 63>>
> 789C0BCE0BAB4A7176CA48CAB53448740FABF474F3720BCC
> B6F4F574CFC888342AC949CE74B50500E1710C0C24000000}
;- Elan [ : - ) ]
author of REBOL: THE OFFICIAL GUIDE
REBOL Press: The Official Source for REBOL Books
http://www.REBOLpress.com
visit me at http://www.TechScribe.com
[9/41] from: rebol:techscribe at: 14-Sep-2000 14:54
Hi Rodney,
I believe REBOL makes a new copy of functions for each derived object.
Let me introduce the players: A root object o, a function f, and a derived
object p:
>> o: make object! [f: func [] [print "a"] ]
>> p: make o []
At this point the two objects are look-alikes:
>> probe o
make object! [
f: func [][print "a"]
]
>> probe p
make object! [
f: func [][print "a"]
]
Let us replace the string "a" by the string "b" in p's function f
>> change at second get in p 'f 2 "b"
== []
Were we successful?
>> probe p
make object! [
f: func [][print "b"]
]
Yes. Was the function f in the parent object o also modified?
>> probe o
make object! [
f: func [][print "a"]
]
No!
Apparently modifications to the inherited function f in p do not propagate
to the f function in the parent object o, ergo the two functions are
independent of each other.
At 02:10 PM 9/14/00 -0700, you wrote:
>Great stuff from both of you - I am thinking of changing my
>INI file handling code to one of these methods (where I have
<<quoted lines omitted: 190>>
> http://www.REBOLpress.com
> visit me at http://www.TechScribe.com
;- Elan [ : - ) ]
author of REBOL: THE OFFICIAL GUIDE
REBOL Press: The Official Source for REBOL Books
http://www.REBOLpress.com
visit me at http://www.TechScribe.com
[10/41] from: rebol:techscribe at: 14-Sep-2000 14:57
Oh, shucks,
a bug:
>assoc-add: func [assoc [block!] candidates [block!] /local found] [
> if is-assoc? assoc [
> foreach [key value] candidates [
> either found: contains-key? assoc key [
> insert at assoc/values index? found value
The line
> insert at assoc/values index? found value
Should be
change at assoc/values index? found value
> ][
> insert assoc/keys key
<<quoted lines omitted: 5>>
> return false
>]
To quote Joel, "silly me" :-).
;- Elan [ : - ) ]
author of REBOL: THE OFFICIAL GUIDE
REBOL Press: The Official Source for REBOL Books
http://www.REBOLpress.com
visit me at http://www.TechScribe.com
[11/41] from: allen:rebolforces at: 15-Sep-2000 8:24
This is a multi-part message in MIME format.
------=_NextPart_000_002F_01C01EEE.52DD2BD0
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
Digging through my own archive. I see that I wrote one last year to behave a
bit more like the collection object in VB. This also allows items to be
added and removed with an index as well, and test for existence.
I never got to thoroughly testing it though, so there are most likely many
ways to fix/improve it.
So if anyone wants to look at combining the two, you are quite welcome too.
Cheers,
Allen K
------=_NextPart_000_002F_01C01EEE.52DD2BD0
Content-Type: text/x-rebol;
name="collection.r"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="collection.r"
REBOL [
Title: "Collection Object"
Author: "Allen Kamp"
Date: 11-Sep-1999
Email: [allenk--powerup--com--au]
Purpose: {To provide an object similar to the VB collection object.}
Notes: {Should make attributes and value lists read only,
Also add a method to return a paired block, for iteration,
and a clone method}
Usage: {
>> col: make collection []
>> col/set "Apple" "Green"
>> col/set "Orange" "Orange"
>> col/set "Grapes" "Purple"
>> col/count
== 3
>> col/get "Apple"
== "Green"
>> col/set "Apple" "Red"
>> col/get "Apple"
== "Red"
>> col/exists? "Apple" ;--Return the index or else false
== 1
>> col/get-item 1
== "Red"
>> col/remove "Apple"
>> col/get "Apple"
== none
;-- if you want to add to the beginning of the list
col/set/pos "Apple" "Granny Smith" 1
;-- to return the list of attributes or values
col/attributes
col/values
}
]
collection: make object! [
values: make block! [] 30
attributes: make block! [] 30
count: func [][length? attributes]
exists?: func [
attribute [any-string!]
/local mark
][
either found? mark: find attributes attribute [return (index? mark)][return false]
]
remove-all: func [][attributes: make hash! [] 30 values: make block! [] 30 exit]
remove-item: func [
key [any-string! integer!]
/local index
][
either integer? key [
index: key
][
if not index: exists? key [exit]
]
remove at attributes index
remove at values index
exit
]
get: func [
{Returns the value for the key or index. Returns none if does not exist}
key [any-string! integer!] {Attribute name string or an index}
/local index
][
if not integer? key [
either index: exists? key [
key: index
][
key: 0
]
]
return pick values key
]
set: func [
key [any-string!] {Attribute name string}
value
/pos position [integer!] {Specify a position in the list}
/local index
][
either pos [
insert at attributes position key
insert/only at values position value
exit
][
either index: exists? key [
change/only at values index value
exit
][
insert tail attributes key
insert/only tail values value
exit
]
]
]
]
------=_NextPart_000_002F_01C01EEE.52DD2BD0--
[12/41] from: ryanc:iesco-dms at: 14-Sep-2000 15:21
This little booger might be handy too. Though it has one peculiarity, dont use
blocks as keys when values might match the contents of the block key.
assoc: make object! [
list: copy []
clear: func [] [list: copy []]
set: func [key value] [ append list key append/only list reduce [value]]
get: func [key /rv] [ first select list key ]
unset: func [key] [ remove/part find t key 2 ]
]
--Ryan
Ryan Cole
Programmer Analyst
www.iesco-dms.com
707-468-5400
The Tao that can be expressed
is not the eternal Tao. -Lao Tzu
[13/41] from: rebol:keithdevens at: 14-Sep-2000 19:13
I really like the path idea. Is there any way to emulate an associative
array without having to make a special object type?
Very unfortunately, Rebol doesn't let you just do things like:
assoc/key: "value"
(if assoc/key doesn't actually exist yet)
is there any way to cheat and do something like:
(fake example that doesn't work)
assoc: make hash! []
change assoc/key "dude"
--------------------------------------------------------------------
It would be really really nice if RT could give us something like this (hint
hint):
assoc: make associative! []
assoc/key: "value"
'first could give us the keys
and 'second could give us the values
Bonus is, it doesn't require any new syntax... no funky assoc{'key'} or
anything. :)
Keith
[14/41] from: ryanc:iesco-dms at: 14-Sep-2000 16:57
FYI: Not quite there, but using the one I just posted allows you to do this:
>> assoc/set 'the "dog"
== [the ["dog"]]
>> assoc/get 'the
== "dog"
>> assoc/list/the
== ["dog"]
>>
[rebol--keithdevens--com] wrote:
> I really like the path idea. Is there any way to emulate an associative
> array without having to make a special object type?
<<quoted lines omitted: 58>>
> > visit me at http://www.TechScribe.com
> >
--
Ryan Cole
Programmer Analyst
www.iesco-dms.com
707-468-5400
The problem of language here are really serious.
We wish to speak in some way about the structure
of the atoms . . . But we cannot speak about atoms
in ordinary language. -W. Heisenberg
[15/41] from: rebol:techscribe at: 14-Sep-2000 18:28
Hi Keith,
You could use the & function like this:
>> assoc: [a 3]
== [a 3]
>> print mold assoc
[a 3]
>> assoc/a
== 3
Now let's add a new key / value pair using &, the new key will be called
new-key
>> & assoc/new-key 4
== [a 3]
>> assoc/new-key
== 4
>> print mold assoc
[new-key 4 a 3]
And another new key/value pair:
>> & assoc/zz "yet another new key/value pair."
== [new-key 4 a 3]
>> assoc/zz
== "yet another new key/value pair."
>> print mold assoc
[zz "yet another new key/value pair." new-key 4 a 3]
We can also add deeper levels:
>> assoc: [a 1]
== [a 1]
>> print mold a
== [a 1]
>> assoc/a
== 1
Let's create a two-level new path that we will address as a/b/c (where b/c
is the new path in a that we are creating)
>> & assoc/b [c 3]
== [a 1]
>> print mold assoc
[b [c 3] a 1]
>> assoc/b
== [c 3]
>> assoc/b/c
== 3
Now for the & function that makes it possible:
&: func ['_p [path!] _v /local _b] [
_b: copy/part :_p (subtract length? :_p 1)
if value? :_b [
insert _b reduce [last :_p _v]
]
]
At 07:13 PM 9/14/00 -0400, you wrote:
>I really like the path idea. Is there any way to emulate an associative
>array without having to make a special object type?
<<quoted lines omitted: 58>>
>> visit me at http://www.TechScribe.com
>>
;- Elan [ : - ) ]
author of REBOL: THE OFFICIAL GUIDE
REBOL Press: The Official Source for REBOL Books
http://www.REBOLpress.com
visit me at http://www.TechScribe.com
[16/41] from: agem:crosswinds at: 15-Sep-2000 5:19
[rebol--keithdevens--com] wrote on 14-Sep-2000/19:13:08-4:00
[17/41] from: agem:crosswinds at: 15-Sep-2000 5:19
[rebol--keithdevens--com] wrote on 14-Sep-2000/19:13:08-4:00
> I really like the path idea. Is there any way to emulate an associative
> array without having to make a special object type?
>
use different types for key/value. for example, use
word! for key, string! for value. this works often,
like i have file! as key and numbers (position etc) as values,
or simple types as keys and blocks as values etc.
'find checks for type, so
find somewhere 'find-me
find somewhere "find-me"
are different.
> Very unfortunately, Rebol doesn't let you just do things like:
> assoc/key: "value"
<<quoted lines omitted: 3>>
> assoc: make hash! []
> change assoc/key "dude"
i use this:
now-is: function [
"change value after key, if it is present, otherwise append both"
series key value] [here]
[
here: find series key
either here [change/only next here value] [append series reduce [key value]]
]
[18/41] from: al:bri:xtra at: 15-Sep-2000 21:22
This is a multi-part message in MIME format.
------=_NextPart_000_04DB_01C01F5B.1A176640
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
I wanted to use integers as keys, so I wrote this up.
[
Rebol [
Title: "Associate"
Name: 'Associate
File: %Associate.r
Author: "Andrew Martin"
Email: [Al--Bri--xtra--co--nz]
Date: 15/September/2000
]
Encapsulate: function [Value] [Block] [
Block: make block! 1 insert/only Block Value Block
]
Associate: function [Block [block!] Key Value] [Index] [
either found? Index: find Block Key [
change/only next Index Encapsulate Value
][
append Block reduce [Key Encapsulate Value]
]
Block
]
Deassociate: function [Block [block!] Key] [Index] [
if found? Index: find Block Key [
remove/part Index 2
]
Block
]
Associated?: function [Block [block!] Key] [Value] [
Value: select Block Key
either none? Value [none] [first Value]
]
]
>> do %Associate.r
>> block: []
== []
>> Associate block 1 "The test is this"
== [1 ["The test is this"]]
>> Associate block 2 [1 2 3]
== [1 ["The test is this"] 2 [[1 2 3]]]
>> associated? block 1
== "The test is this"
>> associated? block 2
== [1 2 3]
>> block/2
== ["The test is this"]
>> deassociate block 2
== [1 ["The test is this"]]
>> deassociate block 1
== []
I don't like 'Encapsulate. Any one know of a better way to do it?
Andrew Martin
ICQ: 26227169
http://members.ncbi.com/AndrewMartin/
http://members.xoom.com/AndrewMartin/
-><-
------=_NextPart_000_04DB_01C01F5B.1A176640
Content-Type: text/x-rebol;
name="Associate.r"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="Associate.r"
[
Rebol [
Title: "Associate"
Name: 'Associate
File: %Associate.r
Author: "Andrew Martin"
Email: [Al--Bri--xtra--co--nz]
Date: 15/September/2000
]
Encapsulate: function [Value] [Block] [
Block: make block! 1 insert/only Block Value Block
]
Associate: function [Block [block!] Key Value] [Index] [
either found? Index: find Block Key [
change/only next Index Encapsulate Value
][
append Block reduce [Key Encapsulate Value]
]
Block
]
Deassociate: function [Block [block!] Key] [Index] [
if found? Index: find Block Key [
remove/part Index 2
]
Block
]
Associated?: function [Block [block!] Key] [Value] [
Value: select Block Key
either none? Value [none] [first Value]
]
]
------=_NextPart_000_04DB_01C01F5B.1A176640--
[19/41] from: al:bri:xtra at: 15-Sep-2000 22:39
This is a multi-part message in MIME format.
------=_NextPart_000_069A_01C01F65.D37B3940
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
I wrote:
> I don't like 'Encapsulate. Any one know of a better way to do it?
I blame the drugs I'm not taking...
Value: reduce [Value]
is a far better way of doing it.
[
Rebol [
Title: "Associate"
Name: 'Associate
File: %Associate.r
Author: "Andrew Martin"
Email: [Al--Bri--xtra--co--nz]
Date: 15/September/2000
]
Associate: function [Block [block!] Key Value] [Index] [
Value: reduce [Value]
either found? Index: find Block Key [
change/only next Index Value
][
append Block reduce [Key Value]
]
Block
]
Deassociate: function [Block [block!] Key] [Index] [
if found? Index: find Block Key [
remove/part Index 2
]
Block
]
Associated?: function [Block [block!] Key] [Value] [
Value: select Block Key
either none? Value [none] [first Value]
]
]
>> do %associate.r
>> block: []
== []
>> Associate block 1 "The test is this"
== [1 ["The test is this"]]
>> Associate block 2 [1 2 3]
== [1 ["The test is this"] 2 [[1 2 3]]]
>> associated? block 1
== "The test is this"
>> associated? block 2
== [1 2 3]
>> block/2
== ["The test is this"]
>> Associate block 'antidisestablishmentarianism [1 "is a long word!" 3]
== [1 ["The test is this"] 2 [[1 2 3]] antidisestablishmentarianism [[1 "is
a long word!" 3]]]
>> Associate block 'anti [3 2 1]
== [1 ["The test is this"] 2 [[1 2 3]] antidisestablishmentarianism [[1 "is
a long word!" 3]] anti [[3 2 1]]]
>> block/anti
== [[3 2 1]]
>> first block/anti
== [3 2 1]
>> deassociate block 2
== [1 ["The test is this"] antidisestablishmentarianism [[1 "is a long
word!" 3]] anti [[3 2 1]]]
>> deassociate block 1
== [antidisestablishmentarianism [[1 "is a long word!" 3]] anti [[3 2 1]]]
>> clear block
== []
Andrew Martin
ICQ: 26227169
http://members.ncbi.com/AndrewMartin/
http://members.xoom.com/AndrewMartin/
-><-
------=_NextPart_000_069A_01C01F65.D37B3940
Content-Type: text/x-rebol;
name="Associate.r"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="Associate.r"
[
Rebol [
Title: "Associate"
Name: 'Associate
File: %Associate.r
Author: "Andrew Martin"
Email: [Al--Bri--xtra--co--nz]
Date: 15/September/2000
]
Associate: function [Block [block!] Key Value] [Index] [
Value: reduce [Value]
either found? Index: find Block Key [
change/only next Index Value
][
append Block reduce [Key Value]
]
Block
]
Deassociate: function [Block [block!] Key] [Index] [
if found? Index: find Block Key [
remove/part Index 2
]
Block
]
Associated?: function [Block [block!] Key] [Value] [
Value: select Block Key
either none? Value [none] [first Value]
]
]
------=_NextPart_000_069A_01C01F65.D37B3940--
[20/41] from: ryanc:iesco-dms at: 15-Sep-2000 11:18
That last little booger I posted didnt let you update values, and crashed when you
asked it a stupid question. This updated version lets you update values, and
returns none when you ask it about a key it has not encountered:
assoc: make object! [
list: copy[]
clear: func[][list: copy[]]
set: func[key value][
if get key[unset key]
append list key
append/only list reduce[value]
]
get: func[key] [
rv: none
error? try [rv: first select list key]
:rv
]
unset: func [key][remove/part find list key 2]
]
You still want to be careful when using block keys.
[ryanc--iesco-dms--com] wrote:
> This little booger might be handy too. Though it has one peculiarity, dont use
> blocks as keys when values might match the contents of the block key.
<<quoted lines omitted: 12>>
> The Tao that can be expressed
> is not the eternal Tao. -Lao Tzu
--ya ya ya
Ryan Cole
Programmer Analyst
www.iesco-dms.com
707-468-5400
The problem of language here are really serious.
We wish to speak in some way about the structure
of the atoms . . . But we cannot speak about atoms
in ordinary language. -W. Heisenberg
[21/41] from: joel:neely:fedex at: 15-Sep-2000 9:25
[rebol--keithdevens--com] wrote:
> It would be really really nice if RT could give us something like this (hint
> hint):
>
> assoc: make associative! []
> assoc/key: "value"
>
> 'first could give us the keys
> and 'second could give us the values
>
Please, no!
First, let me quickly point out my own culpability in submitting a QAD
that used only string! values as keys, but I would NOT like to have a
built-in associative store that only used string! (as in my sketch) or
word! (as implied by the above) values as keys. Any data type
supported by the language should be acceptable as a key. REBOL
shouldn't have any second-class citizens.
Second, as long as REBOL is dependent on the set-*! types, the above
notation has a serious problem. Suppose I had
this-key: ;some complicated expression evaluating to a key
this-val: ;some other complicated expression for a value
I would then be unable to say
assoc/:this-key: this-val
Whatever notation might be in a future version of REBOL for associative
data stores, it should NOT penalize the use of variables.
Third, IMHO it would be VERY bad to force implementation details (such
as how or where keys and values are stored) upon the user of the CONCEPT
of associative array. The words 'first, 'second, etc. describe
position,
which is fine if I'm working on a data structure where the order of the
elements is the most relevant thing to know. I believe them to be
entirely inappropriate when used in a context such as
"I want the key values to this associative store. I happen to
know that the keys are the first component of its representation
(at least in REBOL 7.9) so I'll get them using 'first."
Suppose (this is hypthetical, but please look beyond the specifics
of the example to the underlying principle) Carl and company figure
out a new memory management technique in the course of fixing the
use vs. GC
bug (hint, hint ;-) and decide to change the internal
representation of objects so that the list of words within the object's
context is no longer the "first" attribute of the object. How many
scripts would break if they made that change? OTOH, suppose we had
an interface to objects and functions that would allow one to say
fun-context: get-context some-function
obj-context: get-context some-object
bind-to-context fun-context word-list
bind-to-context fun-context word-list
etc. In other words, real support for context! as a first-class
data type, along with meaningful names for officially sanctioned
interfaces to, and operations using, context! values.
Wouldn't that be clearer to read, easier to learn, and give RT more
opportunities for optimizing/upgrading/tweaking implementations without
any concern that doing so would invalidate existing tricky code?
-jn-
[22/41] from: al:bri:xtra at: 16-Sep-2000 10:51
This is a multi-part message in MIME format.
------=_NextPart_000_0281_01C01FCC.0245EB60
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
And here's a version that's shorter and nicer to use. Can any one think of a
shorter version?
[
Rebol [
Title: "Associate"
Name: 'Associate
File: %Associate.r
Author: "Andrew Martin"
Email: [Al--Bri--xtra--co--nz]
Date: 16/September/2000
]
Associate: function [Block [block!] Key Value] [Index] [
Value: reduce [Value]
either found? Index: find Block Key [
either none? Value [
remove/part Index 2
][
change/only next Index Value
]
][
append Block reduce [Key Value]
]
Block
]
Associate?: function [Block [block!] Key] [Value] [
Value: select Block Key
either none? Value [none] [first Value]
]
]
Andrew Martin
ICQ: 26227169
http://members.ncbi.com/AndrewMartin/
http://members.xoom.com/AndrewMartin/
-><-
------=_NextPart_000_0281_01C01FCC.0245EB60
Content-Type: text/x-rebol;
name="Associate.r"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="Associate.r"
[
Rebol [
Title: "Associate"
Name: 'Associate
File: %Associate.r
Author: "Andrew Martin"
Email: [Al--Bri--xtra--co--nz]
Date: 16/September/2000
]
Associate: function [Block [block!] Key Value] [Index] [
Value: reduce [Value]
either found? Index: find Block Key [
either none? Value [
remove/part Index 2
][
change/only next Index Value
]
][
append Block reduce [Key Value]
]
Block
]
Associate?: function [Block [block!] Key] [Value] [
Value: select Block Key
either none? Value [none] [first Value]
]
]
------=_NextPart_000_0281_01C01FCC.0245EB60--
[23/41] from: g:santilli:tiscalinet:it at: 16-Sep-2000 18:19
Hello [Al--Bri--xtra--co--nz]!
On 15-Set-00, you wrote:
A> Encapsulate: function [Value] [Block] [
A> Block: make block! 1 insert/only Block Value Block
A> ]
>> encapsulate: func [value] [reduce [value]]
>> encapsulate 1
== [1]
>> encapsulate "something"
== ["something"]
>> encapsulate [a block]
== [[a block]]
Regards,
Gabriele.
--
Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer
Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/
[24/41] from: g:santilli:tiscalinet:it at: 16-Sep-2000 18:35
Hello [joel--neely--fedex--com]!
On 15-Set-00, you wrote:
j> Third, IMHO it would be VERY bad to force implementation
j> details (such as how or where keys and values are stored) upon
j> the user of the CONCEPT of associative array. The words
j> 'first, 'second, etc. describe position,
[...]
j> Suppose (this is hypthetical, but please look beyond the
j> specifics of the example to the underlying principle) Carl and
j> company figure out a new memory management technique in the
j> course of fixing the "use vs. GC" bug (hint, hint ;-) and
j> decide to change the internal representation of objects so
j> that the list of words within the object's context is no
j> longer the "first" attribute of the object. How many scripts
Actually, the list of words doesn't have to be the "first
attribute" of an object for FIRST to work. FIRST is an ACTION!, so
a "method" in the value (every REBOL datatype has a table of
methods
that apply to its values). In particular, FIRST is the
>> second :first
== 41
41st function in the table for any value. (Yeah, this is
speculation, but its reasonable, isn't it?)
Of course, I agree that "first" is not a good name for "get the
words of this object". I stongely suggest RT to add:
>> words-of: :first
>> values-of: :second
and so on... (using whatever names they find appropriate)
j> etc. In other words, real support for context! as a
j> first-class data type, along with meaningful names for
j> officially sanctioned interfaces to, and operations using,
j> context! values.
This would be a good thing, too. :-)
Regards,
Gabriele.
--
Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer
Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/
[25/41] from: g:santilli:tiscalinet:it at: 16-Sep-2000 19:09
Hello [Al--Bri--xtra--co--nz]!
On 16-Set-00, you wrote:
A> And here's a version that's shorter and nicer to use. Can any
A> one think of a shorter version?
Yup. ;^)
associate: func [block [block!] key value] [
head change/only any [
find/tail block key
insert tail block key
] reduce [val]
]
associate?: func [block [block!] key] [
all [key: select block key first key]
]
Regards,
Gabriele.
--
Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer
Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/
[26/41] from: al:bri:xtra at: 18-Sep-2000 16:19
Gabriele wrote:
> >> encapsulate: func [value] [reduce [value]]
Thanks Gabriele. I realised that this was simpler:
reduce [value]
Andrew Martin
ICQ: 26227169
http://members.ncbi.com/AndrewMartin/
http://members.xoom.com/AndrewMartin/
[27/41] from: al:bri:xtra at: 18-Sep-2000 16:48
Gabriele wrote:
> Andrew wrote:
> A> And here's a version that's shorter and nicer to use. Can any one
think of a shorter version?
> Yup. ;^)
> associate: func [block [block!] key value] [
<<quoted lines omitted: 6>>
> all [key: select block key first key]
> ]
Thanks Gabriele! I just need to change this line:
] reduce [val]
to:
] reduce [value]
as there's a stray 'VAL running around in Rebol.
Andrew Martin
That Val is not my sister! :-)
ICQ: 26227169
http://members.ncbi.com/AndrewMartin/
http://members.xoom.com/AndrewMartin/
[28/41] from: al:bri:xtra at: 18-Sep-2000 20:04
This is a multi-part message in MIME format.
------=_NextPart_000_02F1_01C021AB.A5365720
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
Here's an implementation for an Associative Data Store (ADS) that uses only
two functions, doesn't require an object per ADS and can use any Rebol value
as a key. Comment invited. Thanks to Gabriele for showing how to shorten the
'Associate? function.
[
Rebol [
Title: "Associate"
Name: 'Associate
File: %Associate.r
Author: "Andrew Martin"
Email: [Al--Bri--xtra--co--nz]
Date: 18/September/2000
]
Associate: function [Block [block!] Key Value] [Index] [
Index: find Block Key
either none? Value [
if found? Index [
remove/part Index 2
]
][
either found? Index [
change/only next Index reduce [Value]
][
append Block reduce [Key reduce [Value]]
]
]
Block
]
Associate?: function [Block [block!] Key] [Value] [
all [
Value: select Block Key
first Value
]
]
]
Andrew Martin
ICQ: 26227169
http://members.ncbi.com/AndrewMartin/
http://members.xoom.com/AndrewMartin/
-><-
------=_NextPart_000_02F1_01C021AB.A5365720
Content-Type: text/x-rebol;
name="Associate.r"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="Associate.r"
[
Rebol [
Title: "Associate"
Name: 'Associate
File: %Associate.r
Author: "Andrew Martin"
Email: [Al--Bri--xtra--co--nz]
Date: 18/September/2000
]
Associate: function [Block [block!] Key Value] [Index] [
Index: find Block Key
either none? Value [
if found? Index [
remove/part Index 2
]
][
either found? Index [
change/only next Index reduce [Value]
][
append Block reduce [Key reduce [Value]]
]
]
Block
]
Associate?: function [Block [block!] Key] [Value] [
all [
Value: select Block Key
first Value
]
]
]
------=_NextPart_000_02F1_01C021AB.A5365720--
[29/41] from: joel:neely:fedex at: 18-Sep-2000 6:47
Here's what happened when I tried it:
>> blk: []
== []
>> Associate blk [2 3] 5
== [[2 3] [5]]
>> Associate? blk [2 3]
== none
Beg pardon? Also...
>> Associate blk 5 7
== [[2 3] [5] 5 [7]]
>> Associate blk [5] 9
== [[2 3] [5] 5 [9]]
>> Associate? blk 5
== 9
>> Associate? blk [5]
== 9
Hmmm... Can't distinguish 5 from [5] ??
>> Associate? blk reduce [2 3]
== none
>> Associate? blk reduce [[2 3]]
== 5
Aha! Unlike all other data types, an extra [] wrapper is require
for block! -type keys? Well, let's check to see if the old
merged-key/value-space bug is still present.
>> Associate? blk reduce [[5]]
** Script Error: first expected series argument of type: series
pair event money date object port time tuple any-function
library struct event.
** Where: all [
Value: select Block Key
first Value
]
So I can't even check to see if [[5]] (or [5]???) might have been
legitimately used as a key?
This version still uses find and therefore still "inherits" the
buggy behavior when block! data are used as keys.
-jn-
[Al--Bri--xtra--co--nz] wrote:
> Here's an implementation for an Associative Data Store (ADS) that uses only
> two functions, doesn't require an object per ADS and can use any Rebol value
> as a key. Comment invited. Thanks to Gabriele for showing how to shorten the
> 'Associate? function.
>
[code snipped]
[30/41] from: lmecir:geocities at: 18-Sep-2000 14:24
Hi Andrew,
there is a lot of cases, where your functions won't work as expected, eg.:
blk: copy []
associate blk [2] 3
associate? blk [2]
The other problem is, that your functions won't accept Any-type! values for
Key/Value
Regards
Ladislav
[31/41] from: rebol:keithdevens at: 18-Sep-2000 10:35
I'm probably going to get beaten up for this :), but if my question is
totally absurd, please excuse my ignorance...
You seem to put a lot of emphasis on being able to use block!s as keys. Is
this really necessary or desired?
It seems like an implementation would be easier and/or more efficient if we
didn't have to worry about that case. Same for logic! values :)
Many other languages get by with just strings for keys, why do we have to be
able to use every Rebol data type as a key?
Keith
[32/41] from: petr:krenzelok:trz:cz at: 18-Sep-2000 17:31
----- Original Message -----
From: <[rebol--keithdevens--com]>
To: <[list--rebol--com]>
Sent: Monday, September 18, 2000 4:35 PM
Subject: [REBOL] Associative Data Store Re:(2)
> I'm probably going to get beaten up for this :), but if my question is
> totally absurd, please excuse my ignorance...
>
> You seem to put a lot of emphasis on being able to use block!s as keys. Is
> this really necessary or desired?
>
> It seems like an implementation would be easier and/or more efficient if
we
> didn't have to worry about that case. Same for logic! values :)
>
> Many other languages get by with just strings for keys, why do we have to
be
> able to use every Rebol data type as a key?
That's true. Even XBase systems work that way. In real - what about any
entry form fiels? They are all strings ... and if not, you can convert to
string first :-)
-pekr-
[33/41] from: ryanc:iesco-dms at: 18-Sep-2000 10:35
Keith and Pek made me realize the obvious. The main problem with select is we
need to use it with two differrent datatypes. Thus so, we can force the key to
string and wrap the data with a block. I believe if we convert the string with
mold we save data type integrity. I think this solves all problems with using
select.
--Ryan
[petr--krenzelok--trz--cz] wrote:
> ----- Original Message -----
> From: <[rebol--keithdevens--com]>
<<quoted lines omitted: 90>>
> > > [code snipped]
> >
--
Ryan Cole
Programmer Analyst
www.iesco-dms.com
707-468-5400
The problem of language here are really serious.
We wish to speak in some way about the structure
of the atoms . . . But we cannot speak about atoms
in ordinary language. -W. Heisenberg
[34/41] from: ryanc:iesco-dms at: 18-Sep-2000 11:06
Here is my very subtle mold change applied to Gabriele's script:
associate: func [block [block!] key value] [
head change/only any [
find/tail block mold key
insert tail block mold key
] reduce [value]
]
associate?: func [block [block!] key] [
all [key: select block mold key first mold key]
]
I cannot find any bugs, someone else want to try?
--Ryan
[ryanc--iesco-dms--com] wrote:
[35/41] from: g:santilli:tiscalinet:it at: 18-Sep-2000 22:05
Hello [Al--Bri--xtra--co--nz]!
On 18-Set-00, you wrote:
A> Thanks Gabriele! I just need to change this line:
A> ] reduce [val]
A> to:
A> ] reduce [value]
Oops! I tested that on the console, with:
associate: func [blk key val] ...
and missed that 'val when changing the function for the list...
:-)
A> as there's a stray 'VAL running around in Rebol.
Some function that lacks a /local declaration...
Regards,
Gabriele.
--
Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer
Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/
[36/41] from: joel:neely:fedex at: 18-Sep-2000 18:27
[rebol--keithdevens--com] wrote:
> I'm probably going to get beaten up for this :), but if my question is
> totally absurd, please excuse my ignorance...
>
The only absurd questions are those that are asked out of malice or not
asked out of timidity. ;-) I may have strong opinions on what I'd like
to be able to do in my own programs, but certainly don't want that to
come across as negative value judgements on anyone who wishes to write
in a different style.
> You seem to put a lot of emphasis on being able to use block!s as keys. Is
> this really necessary or desired?
>
Well, in terms of strictly "necessary", we could just write everything
in
tcl, where all data are strings (even data structures). However, REBOL
gives us this really nice collection of various data types. Why NOT be
able to use them? As the only thing really necessary for looking up a
key is to do an equality test, why shouldn't keys be able to come from
any data type for which the concept of "equal" makes sense?
Also, as one of my co-workers pointed out to me, a block! is a generic
container into which I'm allowed to put any kind of REBOL data I wish.
If find (for example) is defined to work on blocks, then why shouldn't
it work on blocks that contain anything blocks are allowed to contain
(such as other blocks)?
In terms of "desired", I certainly desire that capability!
Let me give an example:
Suppose I have a file of data on school children, which includes age and
grade level for each student. Now suppose I'm asked to cross-tabulate
age and grade level for some report. This means I need a set of
counters,
where each one corresponds to a specific combination of age and grade.
It
would be very pleasant, IMHO, to be able to write something like the
following sketch:
age-grade-tab: assoc/new
foreach studentrecord read/lines %studentdemographics.data [
age: get-age studentrecord
grade: get-grade studentrecord
a-g: reduce [age grade] ; the key IS the combination!
age-grade-tab/put a-g 1 + any [age-grade-tab/get a-g 0]
]
Now someone may ask why I didn't just use a two-dimensional array, with
age and grade as subscripts. A couple of reasons immediately come to
mind:
0) A counter is identified by a combination of two facts: age and
grade. REBOL provides me with a handy datatype for representing
compound values: the block! Why shouldn't I use it?
1) Such an array would be very sparse! As most of the values are
clustered along a narrow diagonal band, it would be nice to limit
my storage requirements to age/grade combinations that actually
exist. (Please remember that this is a small example of a more
general concern. I'm only illustrating the fact that arrays are
often wildly space-inefficient; I'm sure you can all come up with
many examples that are even more extreme, such as weight in grams
versus height in centimeters, etc.)
2) Who says that grade is an integer? Perhaps the coding scheme is
that "K" is kindergarten, 1-8 are grade numbers, "Fr" "So" "Jr"
"Sr" are the last four levels of high school, "GED" high-school
equivalent coursework for adults, and "AE" is adult education
courses for high school diploma holders. In this case we not only
can't use the grade as a numerical index, but now our ranges of
values for both age and grade have become much larger, greatly
aggravating the sparse-array problem above.
3) Notice that the code sketch above is unaffected by changes in
these issues. If I originally wrote the code above only for
an elementary school (ages 6-11, grades 1-6) and was suddenly
asked to produce the same report for an entire traditional system
(ages 4-18, grades 1-12) or a non-traditional school (with the
non-numeric grade codings and ages 4-90), the above code wouldn't
break. Isn't writing generalized code that doesn't break a good
thing to do?
Turning the output side of the report, I can start with something as
simple as
foreach k age-grade-tab/keys [
print [k ":" age-grade-tab/get k]
]
which works for all of the above cases. However, if I need to provide
a more orderly report, I'll probably write something like
foreach k sort/compare age-grade-tab/keys age-grade-sorter [
; with some nice layout tricks not relevant to this point
]
If there is simply a range expansion (grades 1-12 instead of 1-6,
with ages 4-18 instead of 6-11) the above code still works (ignoring
layout issues). If we go to the mixed coding of (2) above, all I
have to do is go to one function age-grade-sorter and adjust it
appropriately. All of the rest of the code still works.
I VERY much like the idea of encapsulating each policy decision in
exactly one place in the code. The idea of having to transform data
back and forth between a natural representation and an agglomerated
string throughout my program (just because some language feature has
a bias in favor of strings) strikes me as very ugly, as well as a
fruitful source of coding and maintenance errors.
I could make up more examples, but I hope the single one above is
adequate to explain the "flavor" of the concept without more belaboring.
> It seems like an implementation would be easier and/or more efficient if we
> didn't have to worry about that case. Same for logic! values :)
>
What worry? REBOL already knows how to take two values and compare
them for equality. The block! and logic! types just happen to
be two examples of that more general principle.
What if I wanted to tally up the birthdates of the students? Isn't
it meaningful to use a date! key for a count of how many students
had a birthday on that date? REBOL already can do that, and equality
comparison on date! values is much more complex that testing
logic! values for equality.
> Many other languages get by with just strings for keys, ...
>
Many other languages "get by" without MOST of the nicer features of
REBOL. If all I wanted was a language which could "get by", I'd
keep writing in C. REBOL has been presented as a high-level language
which can do make use of a wide range of user-friendly data types.
I think it is unfriendly to have arbitrary restrictions on which of
them can be used in some situations.
> ...why do we have to be able to use every Rebol data type as a key?
>
Anyone who wants to use only strings as keys, and can get his/her
work done effectively under that restriction has my blessing. I'm
not trying to force anyone to use any feature they don't see a need
for.
On the other hand, I'll be sad if the hypothetical person in the
previous paragraph tells me that I CAN'T use any other type of key,
even when I can see good reason to do so in my own code.
-jn-
[37/41] from: al:bri:xtra at: 19-Sep-2000 19:36
This is a multi-part message in MIME format.
------=_NextPart_000_02E4_01C02270.F44DE100
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
Here's another version of %Associate.r that should cope with the tests that
Ladislav and Joel have thrown at it.
I haven't yet worked out how to fix Ladislav's concern yet:
> The other problem is, that your functions won't accept Any-type! values
for Key/Value
Comments and criticism gratefully accepted.
I'm sure that my wrapper around 'Find can be done better, too.
[
Rebol [
Title: "Associate"
Name: 'Associate
File: %Associate.r
Author: "Andrew Martin"
Email: [Al--Bri--xtra--co--nz]
Date: 19/September/2000
]
Associate!: make object! [
Find: function [Block [block!] Key] [Index] [
Index: Block
until [
Index: system/words/find/only Index Key
if not found? Index [return none]
either odd? offset? Block Index [
Index: next Index
false
][
true
]
]
Index
]
set 'Associate? function [Block [block!] Key] [Index] [
either found? Index: Find Block Key [
first second Index
][
none
]
]
set 'Associate function [Block [block!] Key Value] [Index] [
Index: Find Block Key
either none? Value [
if found? Index [
remove/part Index 2
]
][
Value: reduce [Value]
either found? Index [
change/only next Index Value
][
append Block reduce [Key Value]
]
]
Block
]
]
]
>> do %associate.r
>> blk: []
== []
>> Associate blk [2 3] 5
== [[2 3] [5]]
>> Associate? blk [2 3]
== 5
>> Associate blk 5 7
== [[2 3] [5] 5 [7]]
>> Associate blk [5] 9
== [[2 3] [5] 5 [7] [5] [9]]
>> Associate? blk 5
== 7
>> Associate? blk [5]
== 9
>> Associate? blk reduce [2 3]
== 5
>> Associate? blk reduce [[2 3]]
== none
>> Associate? blk reduce [[5]]
== none
>> blk: copy []
== []
>> associate blk [2] 3
== [[2] [3]]
>> associate? blk [2]
== 3
Andrew Martin
Nietzchean Rebolutionary...
ICQ: 26227169
http://members.ncbi.com/AndrewMartin/
http://members.xoom.com/AndrewMartin/
-><-
------=_NextPart_000_02E4_01C02270.F44DE100
Content-Type: text/x-rebol;
name="Associate.r"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="Associate.r"
[
Rebol [
Title: "Associate"
Name: 'Associate
File: %Associate.r
Author: "Andrew Martin"
Email: [Al--Bri--xtra--co--nz]
Date: 19/September/2000
]
Associate!: make object! [
Find: function [Block [block!] Key] [Index] [
Index: Block
until [
Index: system/words/find/only Index Key
if not found? Index [return none]
either odd? offset? Block Index [
Index: next Index
false
][
true
]
]
Index
]
set 'Associate? function [Block [block!] Key] [Index] [
either found? Index: Find Block Key [
first second Index
][
none
]
]
set 'Associate function [Block [block!] Key Value] [Index] [
Index: Find Block Key
either none? Value [
if found? Index [
remove/part Index 2
]
][
Value: reduce [Value]
either found? Index [
change/only next Index Value
][
append Block reduce [Key Value]
]
]
Block
]
]
]
------=_NextPart_000_02E4_01C02270.F44DE100--
[38/41] from: al:bri:xtra at: 19-Sep-2000 21:40
> It would be very pleasant, IMHO, to be able to write something like the
following sketch:
> age-grade-tab/put a-g 1 + any [age-grade-tab/get a-g 0]
Will this do?
Associate age-grade-tab a-g 1 + any [associate? age-grade-tab a-g 0]
> foreach k age-grade-tab/keys [
> print [k ":" age-grade-tab/get k]
> ]
How about this?
use [Keys] [
Keys: age-grade-tab
forskip 'Keys 2 [
print [first Keys ":" Associate? Keys]
]
]
> foreach k sort/compare age-grade-tab/keys age-grade-sorter [
> ; with some nice layout tricks not relevant to this point
> ]
foreach [Key Value] sort/compare/skip age-grade-tab age-grade-sorter 2 [
I hope that helps!
Andrew Martin
find/skip select/skip...
ICQ: 26227169
http://members.ncbi.com/AndrewMartin/
http://members.xoom.com/AndrewMartin/
[39/41] from: joel:neely:fedex at: 19-Sep-2000 8:35
Hi, Andrew!
[Al--Bri--xtra--co--nz] wrote:
> > It would be very pleasant, IMHO, to be able to write something like the
> following sketch:
<<quoted lines omitted: 15>>
> > ]
> foreach [Key Value] sort/compare/skip age-grade-tab age-grade-sorter 2 [
Of course! Thank you!
1) The motivation for my original note on the age/grade example was
specifically to explain why I believe there are reasonable uses
for block! data as keys. If memory serves, your Associate and
Associate? functions support that capability. So I think we're in
violent agreement here ;-) since we're dealing with the same
concept.
2) The main difference between our proposals is whether to use a
collection of functions or an object-oriented approach. That
discussion gets us into deeper waters. I tend to use objects
wherever possible for a bunch of reasons:
2.1) I like to keep as much as possible out of the global namespace.
This reduces my risk of name collision when reusing code.
2.2) I like having the data structure definition in the same
physical source file (always!) as the algorithms that work
with/on it. This reduces my risk of inadvertent changes or incon-
sistency when applying functions to independently-built data
structures.
2.3) I like being able to define the required behavior of an
object, and (once I've implemented it) think of the object
ONLY in terms of the interface to invoke those behaviors. This
reduces my risk of breaking code that uses the object if I later
figure out a better implementation and change the "insides" of
the function.
I'll stop with those three, but hope that I've laid the basis for
the real issue -- I think it makes sense to look at programming
from a cost/benefit analysis perspective. The reduction of risk
issues I raised above are benefits (to me, at least -- YMMV)
The cost of using objects (in ANY language) typically includes a
bit of overhead due to additional indirection in accessing the
components of the object. The cost of using objects in REBOL (at
least at THIS point in time) incudes the fact that everything in
an object is cloned when it is used as a template for creating
another object.
For most of my purposes, those costs are not high enough to
outweigh the benefits; I certainly have no quarrel with anyone
whose evaluation of the tradeoff differs. (It does sadden me
when these differences are either ignored or used as the basis
of holy wars, however.)
The bottom line is that I'd prefer a language that gives me as
general and wide-ranging set of concepts as possible, and leaves
the tradeoff decisions to me (on a case-by-case basis, even).
I think that's much better, in general, than having such choices
made in advance by the language (concept, notation, or implemen-
tation) and set in concrete.
-jn-
[40/41] from: brian:hawley:bigfoot at: 19-Sep-2000 22:57
Joel Neely wrote:
>1) The motivation for my original note on the age/grade example was
> specifically to explain why I believe there are reasonable uses
>for block! data as keys. If memory serves, your Associate and
> Associate? functions support that capability. So I think we're in
>violent agreement here ;-) since we're dealing with the same
>concept.
There are only two problems with using blocks as keys.
First, blocks can be recursive, directly or indirectly. You can't
directly compare recursive blocks without crashing REBOL. This is
true not only for the equality operations but also for find and
select as well. I just checked every comparison operator - you
can't even use =? or <>.
Second, the hash! type only hashes on string types. There is no
reason to expect RT to extend hashing to block types - the most
we could hope for would be immediate values and word types. To
hash structured values is an expensive process.
Both these problems are solved by molding any-block! keys. Mold
now works on recursive structures, and the result is a string.
It even allows hashes to optimize lookup of non-strings. This is
only speed-efficient with large amounts of data but at least it
won't crash REBOL.
I'll stay neutral on the cost/benefit analysis of objects versus
global functions. I chalk it up to individual preference.
Brian Hawley
[41/41] from: al:bri:xtra at: 21-Sep-2000 6:17
This is a multi-part message in MIME format.
------=_NextPart_000_00BD_01C02393.AAD19340
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
jn wrote:
> The main difference between our proposals is whether to use a collection
of functions or an object-oriented approach. That discussion gets us into
deeper waters.
In the spirit of putting oil on waters, here's Association.r which allows
the use of objects as well as functions for an Associative Data Store:
[
Rebol [
Title: "Association"
Name: 'Association
File: %Association.r
Author: "Andrew Martin"
Email: [Al--Bri--xtra--co--nz]
Date: 21/September/2000
]
do %Associate.r
Association!: make object! [
Block: block!
Get: func [Key [any-type!]] [
Associate? Block Key
]
Set: func [Key [any-type!] Value [any-type!]] [
Associate Block Key Value
]
]
Association: func [/Size Value [integer!]] [
all [
not Size
Value: 1
]
make Association! [
Block: make block! Value
]
]
]
>> do %Association.r
>> A: Association
>> probe a
make object! [
Block: []
Get: func [Key [any-type!]][
Associate? Block Key
]
Set: func [Key [any-type!] Value [any-type!]][
Associate Block Key Value
]
]
>> a/set [1 2] "hello"
== [[1 2] ["hello"]]
>> a/set [1 5] "1-5"
== [[1 2] ["hello"] [1 5] ["1-5"]]
>> a/get [1 2]
== "hello"
>> a/get [1 5]
== "1-5"
Andrew Martin
Why sleep when I can write Rebol?
ICQ: 26227169
http://members.ncbi.com/andrewmartin/
http://members.xoom.com/AndrewMartin/
-><-
------=_NextPart_000_00BD_01C02393.AAD19340
Content-Type: text/x-rebol;
name="Association.r"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="Association.r"
[
Rebol [
Title: "Association"
Name: 'Association
File: %Association.r
Author: "Andrew Martin"
Email: [Al--Bri--xtra--co--nz]
Date: 21/September/2000
]
do %Associate.r
Association!: make object! [
Block: block!
Get: func [Key [any-type!]] [
Associate? Block Key
]
Set: func [Key [any-type!] Value [any-type!]] [
Associate Block Key Value
]
]
Association: func [/Size Value [integer!]] [
all [
not Size
Value: 1
]
make Association! [
Block: make block! Value
]
]
]
------=_NextPart_000_00BD_01C02393.AAD19340
Content-Type: text/x-rebol;
name="Associate.r"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="Associate.r"
[
Rebol [
Title: "Associate"
Name: 'Associate
File: %Associate.r
Author: "Andrew Martin"
Email: [Al--Bri--xtra--co--nz]
Date: 19/September/2000
]
Associate!: make object! [
Find: function [Block [block!] Key] [Index] [
Index: Block
until [
Index: system/words/find/only Index Key
if not found? Index [return none]
either odd? offset? Block Index [
Index: next Index
false
][
true
]
]
Index
]
set 'Associate? function [Block [block!] Key] [Index] [
either found? Index: Find Block Key [
first second Index
][
none
]
]
set 'Associate function [Block [block!] Key Value] [Index] [
Index: Find Block Key
either none? Value [
if found? Index [
remove/part Index 2
]
][
Value: reduce [Value]
either found? Index [
change/only next Index Value
][
append Block reduce [Key Value]
]
]
Block
]
]
]
------=_NextPart_000_00BD_01C02393.AAD19340--
Notes
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted