[REBOL] Python Dictionaries
From: greggirwin::starband::net at: 22-Sep-2001 12:30
Hi Jason,
<< The two main things I miss from Python:
- dictionaries [associative hash arrays]
- named arguments >>
I've only been at REBOL for a couple months, in my spare time, and I'm
writing little bits and pieces to help me get familiar with it. Here's a
dictionary object I did recently.
; Keys don't *have* to be strings, do they?
; If you assign a block to _data, it's up to you to make it a hash! if you
want.
dictionary: make object! [
; Odd items are keys, even items are values. I.e.
; [key1 value1 key2 value2...]
_data: make hash! 50
clear: does [_data: make hash! 50]
count: does [return (system/words/length? _data) / 2]
length?: :count
empty?: does [return count = 0]
keys: does [extract head _data 2]
values: does [extract next head _data 2]
item: func [key] [
if has-key? key [return first next find/skip _data key 2]
]
has-key?: func [key] [
return found? find/skip _data key 2
]
remove: func [key] [
if has-key? key [system/words/remove/part find/skip _data key 2 2]
]
change: func [key value /only] [
either has-key? key [
either only [
system/words/change/only next find/skip _data key 2 :value
][
system/words/change next find/skip _data key 2 :value
]
][
either only [
append/only _data reduce [key :value]
][
append _data reduce [key :value]
]
]
]
contains?: func [value /only] [
either only [
return found? find/skip/only next _data value 2
][
return found? find/skip next _data value 2
]
]
]
d: make dictionary []
d/change "A" 1
d/change "B" 2
d/item "A"
d/change 0.0.0 "black"
d/item 0.0.0
d/keys
d/values
d/_data: ["A" 1 "B" 2 "C" 3 "D" 4]
d/_data: make hash! ["A" 1 "B" 2 "C" 3 "D" 4]
Let me know what you find wrong with it. It hasn't seen heavy testing yet.
I'm not a python guy so I'd be interested to hear from you, or others, what
you like (or don't) about it.
RE: Named Arguments
I haven't fully grokked the best way to do things in REBOL but refinements
are something you can use where you want to have otpional arguments. I know
it's not the same thing, but often times looking at things from a different
perspective can yield good results. I.e. if you design a function and
determine that most of the time you can use default values for 2 of the 3
parameters, use refinements for those parameters. Andrew posted another
great solution. The way layout works, you can pass arguments in any order
and, as long as they are different data types, it will figure out where they
belong.
I'd bet you could also write a wrapper that could take as parameters a
function and a block of named arguments for that function. Impossible you
say? Ahhhh, remember that REBOL is highly reflective and you can, at
runtime, acquire the interface spec for a function.
>> first :length?
== [series]
>> first :find
== [series value /part range /only /case /any /with wild /skip size /match
/tail /last /reverse]
>> first :append
== [series value /only]
<...OK, so my interest was piqued and I had to see how this might be
done...time passes...>
At the risk of posting bad information, here is what I came up with. Bear in
mind that I am a beginner at REBOL and this is surely not going to work in
all cases you could throw at it. That said:
; Ladislav Mecir's nargs function
nargs: func [
{The number of the function arguments}
f [any-function!]
] [
-1 + index? any [find first :f refinement! tail first :f]
]
do-fn-named-args: func [fn[action! function!] args[block!] /local arg
fn-args] [
fn-args: array nargs :fn
foreach arg args [
either (type? (second arg)) = word! [
change/part (at fn-args index? (find first :fn (to-word first
arg))) (get second arg) 1
][
change/part (at fn-args index? (find first :fn (to-word first
arg))) (second arg) 1
]
]
do reduce [:fn fn-args]
]
a: [1 2 3 4 5]
do-fn-named-args :append [['value 1] ['series a]]
do-fn-named-args :length? [['series a]]
Note that I didn't bother to come up with a special syntax, handle leading
ordinal parameters, or deal with refinements. Also note that you use a
leading colon on the function, and the param names are words. Arguments you
don't supply will be none in the function call. You could also pass a
dictionary as your list of named arguments and iterate over the function
parameters doing a dictionary lookup for each parameter.
So, how useful will this be in REBOL? I'm not sure. Most REBOL functions
take a very small number of arguments, which is a good thing. This kind of
obviates the need for the simplifications afforded by named arguments.
Still, it's something to keep in our bag of tricks.
--Gregg