|
Seb Wizard
Joined: 14 Aug 2004 Posts: 1269
|
Posted: Mon Oct 01, 2007 7:46 pm
Feature suggestion: More database record functions |
I was reading this in the zMUD forum:
Arminas wrote: |
Code: |
#ALIAS addname {#addkey names %1 %2;#untrigger SubName;#gag;#REGEX "SubName" {\b(%expanddb(@names,'|','|'))\b} {#sub {~%1~%if(~%iskey(~@names,~%1),~" -=(~"~@names.~%1~")=-~")};#cw 12}} |
This code creates an alias. The alias when used creates the variable you need and the trigger that goes with it.
So if you want to edit the trigger you will need to edit the alias. Each time you run the alias it deletes and recreates the trigger.
Sorry this is the easiest way to do what you wanted. Turning on and off the trigger was not causing it to recompile as I had hopped it would. |
... and I was thinking isn't there a better way? Does zMUD not have a function to get the keys and / or the values of a database record variable? (Answer: No.) I don't know if it can be done in CMUD without deleting and readding the trigger every time? But zMUD / CMUD database record variables are essentially the same as Python "dictionaries" (or "mappings"). There are 21 operations that are possible on a Python mapping, but only 6 in CMUD.
Some examples of some useful functions that might be added:
%dbkeys(dbrec)
Returns the keys of the database record with pipes deliminating the keys, suitable for using in a trigger pattern.
%dbvalues(dbrec)
Returns the values of the database record with pipes deliminating the values, suitable for using in a trigger pattern.
%dbitems(dbrec)
Returns the keys and values of the database record with pipes deliminating the keys and values, suitable for using in a trigger pattern.
Or check out the table to see what is available in Python for more ideas. |
|
|
|
Progonoi Magician
Joined: 28 Jan 2007 Posts: 430
|
Posted: Mon Oct 01, 2007 8:50 pm |
I wholeheartly support this idea!!
Prog |
|
_________________ The Proud new owner of CMud.
--------------------------------
Intel Core i5-650 3,2GHz
4 DD3 RAM
GTX 460 768MB
Win 7 Home Premium 64x
-------------------------------- |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Wed Oct 03, 2007 4:26 am |
I'll definitely take a look at this. But also keep in mind that the full Lua support in v2.0x treats CMUD database variables directly as Lua tables, so you can also use any of the existing Lua methods for working with these "tables". Lua doesn't have all of the routines that Python has, so I'll definitely give the list you posted some consideration in the future. I was hesitant to do this kind of stuff in the past because CMUD database variables were so inefficient, but now that we have hashed tables in 2.0x, it's worth adding more support for these useful "tables" (or "dictionaries", whatever terms you prefer).
Btw, you might not be aware that in CMUD (even in 1.34 I think), you can use the syntax {@varname} in a trigger pattern even if @varname is a database variable! We all know that for string lists this works, but some people don't know that when using a database variable, CMUD converts this to {key1|key2|key3...} just like your %dbkeys example. So, you can already do something like this:
Code: |
#TRIGGER {({@dbvar}) whatever} {#SHOW "Value is:" %db(@dbvar, %1)} |
|
|
|
|
Seb Wizard
Joined: 14 Aug 2004 Posts: 1269
|
Posted: Wed Oct 03, 2007 9:29 am |
http://lua-users.org/wiki/TablesTutorial wrote: |
All Lua tables are actually dictionaries |
Heh.
Zugg wrote: |
Btw, you might not be aware that in CMUD (even in 1.34 I think), you can use the syntax {@varname} in a trigger pattern even if @varname is a database variable! We all know that for string lists this works, but some people don't know that when using a database variable, CMUD converts this to {key1|key2|key3...} just like your %dbkeys example. So, you can already do something like this:
Code: |
#TRIGGER {({@dbvar}) whatever} {#SHOW "Value is:" %db(@dbvar, %1)} |
|
I didn't know that! So that covers one of the reasons for making the %dbkeys() function, and presumably makes it trivial to add it as a stand-along function (since it is already coded).
We could also have %isvalue() (which wouldn't be terribly fast, because values of Lua table are presumably not indexed so it would have to iterate to check). And:
Code: |
#TRIGGER {({%dbitems(@dbvar)}) whatever} {#sub {%1 %if(%iskey(@dbvar,%1)," -=("@dbvar.%1")=-", <INSERT CODE TO DO A REVERSE LOOKUP ON DBVAR>)};#cw 12}} |
This is for a mapping (associative array) of e.g. character names. The code that I haven't written out would use %isvalue(). |
|
|
|
Fang Xianfu GURU
Joined: 26 Jan 2004 Posts: 5155 Location: United Kingdom
|
Posted: Wed Oct 03, 2007 2:32 pm |
Zugg wrote: |
Lua doesn't have all of the routines that Python has |
By design. Looking at that list, Lua does have most of those functions, or something that'll do the same thing, and the ones it doesn't would be pretty simple to write yourself if you really needed them. The same list in lua would look something like:
- #a
- a[k] (or a.k if k is a string)
- a[k] = v
- a[k] = nil
- Not included - write your own with for or just do a = {} if you can
- b = a
- if a[k] then whatever end
- if not a[k] then whatever end
- if a[k] then whatever end
- Not included, but probably not needed, and easily done with for.
- Not included, for again.
- Not included, for.
- Not sure what that one does.
- For again
- a[k] or x
- Expansions of the above
- Ditto.
- for k,v in pairs(a)
- for k,v in pairs(a)
- for k in pairs(a)
- for _,v in pairs(a)
Notice how the last four are essentially identical in Lua? For is the solution to all man's problems. |
|
|
|
Vijilante SubAdmin
Joined: 18 Nov 2001 Posts: 5182
|
Posted: Wed Oct 03, 2007 11:44 pm |
The only addition I really need so far is to be able to do a key lookup based on value. There just are so many things where both key and values are paired and unique. The alternative to being able to do such a lookup quickly is to use 2 variables, one mirroring the others for position. Currently I commonly use a combination of %expanddb and %regex, but there are some very blatant performance hits for doing that way.
Something like this:
#ADDKEY blah {Zugg=Great|Chiara=Wonderful|Vijilante=Needy}
#SHOW %keyof(@blah,"Wonderful")
Would display "Chiara". |
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
|
Seb Wizard
Joined: 14 Aug 2004 Posts: 1269
|
Posted: Thu Oct 04, 2007 12:31 am |
Edited quoted text for brevity. Italics mine.
Fang Xianfu wrote: |
Zugg wrote: |
Lua doesn't have all of the routines that Python has |
By design. Looking at that list, Lua does have most of those functions, or something that'll do the same thing, and the ones it doesn't would be pretty simple to write yourself if you really needed them. The same list in lua would look something like:
- a[k] = nil
Does that actually delete the key, value pair, or just the value though? (The Python example deletes both.)
- b = a
Does that not just make b contain a reference to a? (The Python example creates a copy, but if your code was Python, b would now refer to a - which I think makes more sense.)
For is the solution to all man's problems. |
Right. Most languages have the "for" construct. One can do most of these things in Python too using "for", but one of the beauties of Python is that so much has been added to reduce the amount of code that needs to be written (and hence the number of bugs, and, as a result of both, the time spent developing something). Anyway, I don't want to get into a language war, and this thread certainly isn't the place for it even if I did. |
|
|
|
Seb Wizard
Joined: 14 Aug 2004 Posts: 1269
|
Posted: Thu Oct 04, 2007 12:58 am |
Vijilante wrote: |
The only addition I really need so far is to be able to do a key lookup based on value. There just are so many things where both key and values are paired and unique. The alternative to being able to do such a lookup quickly is to use 2 variables, one mirroring the others for position. |
I just thought I'd do a 30 second search for a Python solution and there's a package to do two_way_mappings:
http://pypi.python.org/pypi/two_way_dict/0.2
It's really simple actually - it's just like you suggested - use two variables that mirror each other. But I still like the fact that someone else has done it and implemented the unit tests and all the methods.
I should really try out using Python in my CMUD scripts... I feel another thread coming on... |
|
|
|
Fang Xianfu GURU
Joined: 26 Jan 2004 Posts: 5155 Location: United Kingdom
|
Posted: Thu Oct 04, 2007 1:10 am |
A language war'd be pretty pointless since CMUD is going to fully support Python via WSH in the future, so no worries on that one :) I wouldn't advise trying to use it yet, though Seb - the only thing you can edit at the moment is variables. It might be possible to do some interesting stuff with expression triggers, but it'd be way more complex than just using the new zs object when it's made available via WSH.
Regardless, I wasn't refuting that Python's a good system - it must be, or people wouldn't use it. I was following from Zugg's comment about Lua being able to manipulate CMUD's database and string list variables, and that despite what Zugg said about Lua not including all those operations by default, it's still capable of them. |
|
|
|
Seb Wizard
Joined: 14 Aug 2004 Posts: 1269
|
Posted: Thu Oct 04, 2007 2:12 am |
Right. Now I remember why I never got into using other scripting languages in zMUD (plus I thought (probably wrongly) that they would be slower). Maybe I'll do some quick performance comparison tests though when the next CMUD version is out. It might be interesting. The problem I suppose is making a variety of benchmark tests if one was do a thorough comparison.
From an academic perspective I'm still interested in the answers to those two questions in italics in the section where I quoted you though. |
|
|
|
Fang Xianfu GURU
Joined: 26 Jan 2004 Posts: 5155 Location: United Kingdom
|
Posted: Thu Oct 04, 2007 2:23 am |
Oh, I didn't realise there were questions there, I just thought you'd truncated my post.
1) Short answer: yes. Long answer: deleting a value without deleting the key doesn't make sense, at least not as I understand it. Variables aren't initialised - they simply don't exist if they point to nil. It's impossible for a variable to have no value (counting nil as a value).
2) No, it wouldn't create a copy - I wasn't sure what the Python function was for - but again, you can do that simply with for. |
|
|
|
Seb Wizard
Joined: 14 Aug 2004 Posts: 1269
|
Posted: Thu Oct 04, 2007 2:42 am |
Fang Xianfu wrote: |
1) Short answer: yes. Long answer: deleting a value without deleting the key doesn't make sense, at least not as I understand it. Variables aren't initialised - they simply don't exist if they point to nil. It's impossible for a variable to have no value (counting nil as a value). |
I understand. Python's the same in that respect. But other languages can have variables with no value so I wasn't sure whether this was the case with Lua. |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Thu Oct 11, 2007 10:12 pm |
I have added the following functions to v2.06:
Code: |
#ADDKEY dbvar Name Zugg
#ADDKEY dbvar Race Dwarf
#ADDKEY dbvar Level 10 |
- %dbkeys(@dbvar)
- Returns a string list of keys in the database variable. Example: "Name|Race|Level"
- %dbvalues(@dbvar)
- Returns a string list of values in the database variable. Example: "Zugg|Dwarf|10"
- %isvalue(@dbvar,Value)
- Returns the index of the key that contains the value. Returns zero (false) if not found. Example: %isvalue(@dbvar,"10") returns 3
- %iskey(@dbvar,Key)
- Fixed this existing function to return the proper index of the matching key (as versions prior to 2.00 did)
- %item(@dbvar,n)
- Improved this existing function to return the nth key of a database variable. Example: %item(@dbvar,3) returns "Level"
Note that in all cases, the value of the "nth" item of a database variable depends upon the internal hash order and has nothing to do with the order in which items were added, nor does it have anything to do with the sorted order of the record. So the index for %item really needs to come from either %iskey or %isvalue, and if the database variable is modified, these index values might change.
Anyway, that should take care of the other functions that Seb was looking for. |
|
|
|
Iceclaw Apprentice
Joined: 11 Sep 2005 Posts: 124
|
Posted: Thu Oct 11, 2007 10:40 pm |
Awesome, I'll definately be making use of these new functions in my future scripting!
|
|
|
|
Seb Wizard
Joined: 14 Aug 2004 Posts: 1269
|
Posted: Fri Oct 12, 2007 1:34 am Re: Feature suggestion: More database record functions |
Zugg wrote: |
Anyway, that should take care of the other functions that Seb was looking for. |
Cool, but not this one.
Seb wrote: |
%dbitems(dbrec)
Returns the keys and values of the database record with pipes deliminating the keys and values, suitable for using in a trigger pattern. |
Let me clarify it:
%dbitems(@dbvar)
Example:
%dbitems(@dbvar) from your example @dbvar would return either "Name|Race|Level|Zugg|Dwarf|10" or "Name|Zugg|Race|Dwarf|Level|10". It doesn't really matter which it returns, but the idea is you could use it in a trigger pattern, for example, if you have a list of character names (or aliases or alternative personas, "alts") that relate to each other, but there is no obvious key to use for the value, as one usually doesn't know the real names of the players behind the characters, and therefore the key would just be one of the names.
Example:
Code: |
#ADDKEY friends Tom "Dick|Harry"
#ADDKEY friends Hook "Line|Sinker"
#ADDKEY friends Game "Set|Match"
|
Maybe you would make a trigger to highlight their names in the WHO list or when they use a global chat channel...
Hmm, I suppose one could put one of the character names in as the key and as one of the values (in the nested string list), but it would increase the memory storage requirements unnecessarily. Or maybe you have a better idea for an efficient way of doing this? I suppose for this particular case, one could just have a string list with all one's friends in, but it is also useful to know your friends alts. Maybe you would have a tooltip on any one of their names showing their alts.
Also, another idea:
%dbitem(@dbvar,Key or Value[,Return Both])
%dbitem(@dbvar,"Tom") returns "Dick|Harry"
%dbitem(@dbvar,"Dick|Harry") returns "Tom"
%dbitem(@dbvar,"Tom",True) returns "Tom|Dick|Harry"
I was thinking of a more complex version of dbitem that would allow searching within the stringlist value for a particular item, but I suppose one could nest a few functions to do this, e.g.
%item(@dbvar,%ismember("Dick",%dbitems(@dbvar))) --> returns "Tom|Dick|Harry"
or use %find with a database.
Actually this particular example is probably better defined by a database, since dbvars don't really allow for one-to-many or many-to-many relationships, and that would probably be a more suitable schema for this example. I've never really used databases in zMUD or CMUD as in zMUD there seemed to be too much to learn for the value gained in my usage (and also in CMUD we all know that the database module is going to rewritten). I know SQL (spent most of the past two days writing in it) so CMUD database stuff should be easy for me once it is rewritten (and I might not need to read pages of documentation to get started).
I suppose a datatype where you can have non-unique keys is not practical? (So we can have multisets.) Because that would be another solution. Or a datatype with a double unique index (composite index) - unique on a hash of both "columns", rather than just the first one.
So anyway, these new functions you have already added will certainly be useful, and I'd be interested in your thoughts on this problem. |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Fri Oct 12, 2007 2:14 am |
Well, @dbitems would certainly be easy to add, although I'm still a bit perplexed by your example and why you'd ever need it. You could also just do a %replace(@dbvar,"=","|") to get it already, although I guess that wouldn't work if there were embedded = within items.
The problem with %dbitems is that you would lose any sort of index reference to the original variable. With %iskey and %isvalue, you get the Index value that you can use with %item to index into the %dbkeys and %dbvalues list. With @dbitems, you just end up with a string list with no structure, and I'm just having a hard time seeing the usefulness of it.
I'm not going to do the %dbitem function idea though...that's just too complex and un-intuitive. And it shouldn't be needed given the other new functions that were added. Your first example is the same as %db(@dbvar,"Tom"), your second example is the same as %item(%dbkeys(@dbvar),%isvalue(@dbvar,"Dick|Harry")) and your 3rd example is just a concat of these two.
And no, you cannot have non-unique keys...that's the whole purpose of having hashed database variables. These work like tables in Lua, or associative arrays in Perl. The key *must* be unique. |
|
|
|
Seb Wizard
Joined: 14 Aug 2004 Posts: 1269
|
Posted: Fri Oct 12, 2007 2:59 am |
Zugg wrote: |
Well, @dbitems would certainly be easy to add, although I'm still a bit perplexed by your example and why you'd ever need it. You could also just do a %replace(@dbvar,"=","|") to get it already, although I guess that wouldn't work if there were embedded = within items. |
Would that work within a trigger pattern? Even if @dbvar changes? If so, then fine. Although, %dbitems is easier than your example for those of us who don't have the entire zScript language in our heads!
Zugg wrote: |
The problem with %dbitems is that you would lose any sort of index reference to the original variable. With %iskey and %isvalue, you get the Index value that you can use with %item to index into the %dbkeys and %dbvalues list. With @dbitems, you just end up with a string list with no structure, and I'm just having a hard time seeing the usefulness of it. |
I thought I illustrated a use: when you just want to know if something is in your @dbvar, but you are using a trigger pattern (which will fire or not fire). Possibly this is too limited a use to warrant it's own function, I dunno. It's just an idea.
Zugg wrote: |
I'm not going to do the %dbitem function idea though...that's just too complex and un-intuitive. And it shouldn't be needed given the other new functions that were added. Your first example is the same as %db(@dbvar,"Tom"), your second example is the same as %item(%dbkeys(@dbvar),%isvalue(@dbvar,"Dick|Harry")) and your 3rd example is just a concat of these two. |
If @dbvar is updated, will a trigger pattern containing %item(%dbkeys(@dbvar),%isvalue(@dbvar,"Dick|Harry")) also be updated automatically? (And a friend list system is something a lot of people want to make and it seems quite tricky to code. %concat(%db(@dbvar,"Tom")),%item(%dbkeys(@dbvar),%isvalue(@dbvar,"Dick|Harry"))) merely illustrates this. Not to mention containing three references to @dbvar - that seems pretty inefficient code. I'm sure a single function to do all that would run faster.)
Zugg wrote: |
And no, you cannot have non-unique keys...that's the whole purpose of having hashed database variables. These work like tables in Lua, or associative arrays in Perl. The key *must* be unique. |
I wasn't talking about the existing database record variables here, but new datatypes to solve a problem not easily covered by the existing non-#DB functionality, and where the #DB functionality seems a bit like overkill. |
|
|
|
Fang Xianfu GURU
Joined: 26 Jan 2004 Posts: 5155 Location: United Kingdom
|
Posted: Fri Oct 12, 2007 3:01 am |
There was mention of using %expanddb in the trigger pattern to get the effect that Seb describes, but it doesn't work.
%item(@dbvar,%ismember("Dick",%dbitems(@dbvar))) doesn't seem like it'll do what you want. Like Zugg says, the stringlist returned by your %dbitems won't be linked in any way to the actual variable - there's no way to put the %ismember number back into the variable, because if your first key is a stringlist of 20 items, then the second key will have an index of 22, which is meaningless to the %item function. The functionality you seem to want (give it a value, it gives you the key) is already easily done with %item(@dbvar,%isvalue(@dbvar,"something")).
However, one thing. %isvalue returns "the key" that's associated with a value, but values don't need to be unique - it's possible that you'll get more than one result. Will %isvalue return a stringlist in that case? |
|
|
|
Fang Xianfu GURU
Joined: 26 Jan 2004 Posts: 5155 Location: United Kingdom
|
Posted: Fri Oct 12, 2007 3:03 am |
Also, the %replace example you gave is very much not working, Zugg. I'd rather %expanddb or %replace worked, rather than making a new function.
Also, Seb, it seems weird that you want to use %dbitems rather than %dbkeys for your friend list, Seb. How would it work?
#additem Friends.Citymates %1
You wouldn't want the key in that situation. Unless you mean something like:
#additem Friends.Harry {List|of|Harry's|Alts}, in which case you could just have the name "Harry" in the value list as well. |
|
|
|
Seb Wizard
Joined: 14 Aug 2004 Posts: 1269
|
Posted: Fri Oct 12, 2007 10:39 am |
Fang Xianfu wrote: |
Unless you mean something like:
#additem Friends.Harry {List|of|Harry's|Alts}, in which case you could just have the name "Harry" in the value list as well. |
Yes, that's what I mean, and I did come up with that solution myself, but as I mentionned it's not ideal:
Seb wrote: |
... but the idea is you could use it in a trigger pattern, for example, if you have a list of character names (or aliases or alternative personas, "alts") that relate to each other, but there is no obvious key to use for the value, as one usually doesn't know the real names of the players behind the characters, and therefore the key would just be one of the names.
Example:
Code: |
#ADDKEY friends Tom "Dick|Harry" |
...
Hmm, I suppose one could put one of the character names in as the key and as one of the values (in the nested string list), but it would increase the memory storage requirements unnecessarily. |
Maybe that's a small price to pay though. Or maybe a DB record variable is just not going to be ideal for this. |
|
|
|
Fang Xianfu GURU
Joined: 26 Jan 2004 Posts: 5155 Location: United Kingdom
|
Posted: Fri Oct 12, 2007 12:53 pm |
Eh, a few bytes per person isn't going to make much of a difference. If you already have your dbvar set up without the key in the value list, it's easily added with #loopdb anyway.
|
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Fri Oct 12, 2007 5:42 pm |
Quote: |
However, one thing. %isvalue returns "the key" that's associated with a value |
No, %isvalue returns the *index* of the key, not the key itself. To get the key, you would use %item(@dbvar,%isvalue(@dbvar,"value"))
I'll do %dbitems because it's easy and saves a concat. Normally, I do not recommend putting function calls into a trigger pattern. The regular expression library obviously doesn't know anything about CMUD variables or functions, so they need to be expanded to create the trigger pattern. It *should* update the pattern if a nested variable changes, but that's something I haven't done a lot of testing with.
Quote: |
Also, the %replace example you gave is very much not working, Zugg |
Maybe it's a bug that was already fixed in v2.06. But I just did this:
Code: |
#addkey dbvar name zugg
#addkey dbvar race dwarf
#addkey dbvar level 10
#show %replace(@dbvar,"=","|") |
and it properly displayed "level|10|name|Zugg|race|dwarf" |
|
|
|
Fang Xianfu GURU
Joined: 26 Jan 2004 Posts: 5155 Location: United Kingdom
|
Posted: Fri Oct 12, 2007 5:54 pm |
Sorry, yes, I meant index. My point remains the same:
#addkey Names Friends Zugg
#addkey Names CityMates Zugg
#show %isvalue(@Names,"Zugg")
Will it return both key indices?
And I was talking about %replace in trigger patterns. I didn't realise that functions like that weren't allowed - I guess adding %dbitems won't help with Seb's problem, then, because you'll still have to use a variable to store the %dbitems stringlist in. |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Fri Oct 12, 2007 6:24 pm |
Quote: |
Will it return both key indices? |
Ahh, I see what you are saying. No, it will only return the first index that it finds (the lowest index number). Since %isvalue is an integer function, there isn't any way to return multiple values. This is part of the reason why I never bothered with such functions in the first place since their usefulness is rather limited.
Functions *are* allowed in patterns for the most part. Check the Compiled Pattern tab to see how the pattern is being compiled. The issue is that the pattern isn't necessarily updated properly when variables embedded in function calls change, and that's something that is on the bug list (it effects functions in the status bar in a similar way for example) |
|
|
|
Arminas Wizard
Joined: 11 Jul 2002 Posts: 1265 Location: USA
|
Posted: Fri Oct 12, 2007 6:34 pm |
Hey guys... I don't suppose any of you have gone back and looked at the thread I answered that started this whole conversation?
I *did* find a better way to solve that particular problem.
Also keep in mind that I tend to think outside the box/rules. This sometimes causes me to miss the obvious as I did in this problem to begin with.
#function addName {#addkey spys %1 %2;names=%replace(@spys,"=","|")}
#function delName {#delkey spys %1 %2;names=%replace(@spys,"=","|")}
#regex spytrig {\b@names\b} {Zugg's nice new functions do away with most of the nonsense I had here before} |
|
_________________ Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram |
|
|
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|