Chr function

jaynabonne
18 Dec 2012, 20:59What is the use of the Chr function?
As an example, if I do:
msg(Chr(65))
I would expect (or hope) it would print an "A". Instead it just prints "65". So it seems a "Chr" is just returning an integer, but then what use is it?
Another "Chr" clue: if you iterate through a string with "foreach", it gives you "Chars" back. You can tell that because if you try to pass it to Asc, then it says it can't find function "Asc(Char)". However, if I call "Asc(Chr(65))", it says it can't find function "Asc(Int32)". That seems to me to imply that Chr is just returning in Int32. Perhaps my confusion is coming in because it's a bug?
So... is there a way to create a string with a character having a specific ASCII value?
As an example, if I do:
msg(Chr(65))
I would expect (or hope) it would print an "A". Instead it just prints "65". So it seems a "Chr" is just returning an integer, but then what use is it?
Another "Chr" clue: if you iterate through a string with "foreach", it gives you "Chars" back. You can tell that because if you try to pass it to Asc, then it says it can't find function "Asc(Char)". However, if I call "Asc(Chr(65))", it says it can't find function "Asc(Int32)". That seems to me to imply that Chr is just returning in Int32. Perhaps my confusion is coming in because it's a bug?

So... is there a way to create a string with a character having a specific ASCII value?
Alex
19 Dec 2012, 11:36Bugs bugs everywhere - it's like an episode of "I'm a Celebrity"...
It would seem that the Chr function has never worked, so good find! Checked in a fix now.
As for foreaching through a string, is this useful? Because a string implements IEnumerable in .net, it's returning Chars here, but Quest hasn't got any support for Chars which is the reason for #1201 which you logged on the Issue Tracker. Either we support this and convert the char to a string, or we throw an exception in this case and you can use a for loop with the Mid function to go through a string character by character.
It would seem that the Chr function has never worked, so good find! Checked in a fix now.
As for foreaching through a string, is this useful? Because a string implements IEnumerable in .net, it's returning Chars here, but Quest hasn't got any support for Chars which is the reason for #1201 which you logged on the Issue Tracker. Either we support this and convert the char to a string, or we throw an exception in this case and you can use a for loop with the Mid function to go through a string character by character.

jaynabonne
19 Dec 2012, 12:53I think as long as you can use Chars in some limited String contexts, more or less, it *can* be useful. (And without that ability, Chr would be useless, since you wouldn't be able to do anything with the return value.) For example, you could do a foreach on the string and weed out or replace characters. I know you can still use Mid, but something feels more efficient with characters rather than unit-length strings. 
As far as 1201 goes, I think I found it a bit disconcerting that a valid Quest object (of any type) passed to TypeOf would generate an exception - given that the only way to avoid it is to know what type the thing is ahead of time, which is the whole point of TypeOf to begin with, for those times you don't know. I'd even be happy with TypeOf returning an empty string or null instead of an exception, so that I can at least skip bad types if need be.

As far as 1201 goes, I think I found it a bit disconcerting that a valid Quest object (of any type) passed to TypeOf would generate an exception - given that the only way to avoid it is to know what type the thing is ahead of time, which is the whole point of TypeOf to begin with, for those times you don't know. I'd even be happy with TypeOf returning an empty string or null instead of an exception, so that I can at least skip bad types if need be.

jaynabonne
19 Dec 2012, 12:58An alternative would be to have Chr return a unit-length string instead of a character. That would make it (more or less) the inverse of Asc and might make things more consistent.
Alex
19 Dec 2012, 16:12The fix I checked in makes Chr return a string.
I think generic dictionaries are the only type that Quest "understands" that will cause an error with TypeOf, and player.currentcommandresolvedelements should be the only place where this type exists. It's sort of a not-really supported type, as you can't save it (you'll find that player.currentcommandresolvedelements gets set to null when saving a .quest-save file). But that's really just because I haven't got round to adding support for saving and loading this type yet, or handling it in the attributes editor, because I don't think most games need this type (string dictionaries and object dictionaries should be sufficient - if you really need a dictionary that can hold either... why?)
I think generic dictionaries are the only type that Quest "understands" that will cause an error with TypeOf, and player.currentcommandresolvedelements should be the only place where this type exists. It's sort of a not-really supported type, as you can't save it (you'll find that player.currentcommandresolvedelements gets set to null when saving a .quest-save file). But that's really just because I haven't got round to adding support for saving and loading this type yet, or handling it in the attributes editor, because I don't think most games need this type (string dictionaries and object dictionaries should be sufficient - if you really need a dictionary that can hold either... why?)

jaynabonne
19 Dec 2012, 17:49Going off the last part, I had explored (and initially used) generic dictionaries briefly, before I discovered by looking in the code that they didn't serialize. My case: for conversations, I wanted (and still want) to allow topics to have associations. For example, if you ask someone about her father, she might say, "My father was a farmer from Tibo." That would have a strong topic value for "father" (since that's the requested topic), but there might be auxilliary topics brought in for things like "farming", "family", "Tibo", "mother", "sister", etc, all with relevant weights. That way, if the NPC offers additional chit-chat, she can pick something to say that is not only based on "father", but related in some way to what has been discussed. (And the topics would merge and disappear over time, similar to the way a human mind works).
The natural data structure to me for that is a vector of weights (or a set of topic/weight pairs), which expresses in Quest efficiently as a dictionary of doubles. And the generic dictionary supports adding any type as a value, so I was able to use it. But... it doesn't save. So now, since I want this to persist across turns and also be saved, I have to keep the weight vector persistently as a string dictionary, and then convert it back and forth to a numeric one when I use it. It's a pain and who knows what sort of performance problems that will cause if I have to, say, search through several hundred "phrases" to decide which one a character will say.
The bottom line: people will always want what you can provide as far as functionality, especially as Quest apps become more complex, especially if it is a generalzation of an existing feature. I know I would go crazy if I could have arbitrary nesting of dictionaries or lists (i.e. dictionaries whose elements are dictionaries, and lists of lists could support multi-dimensional arrays). As of now, I would have to create objects at each level. Which I can do and have done, but it suppresses the design a bit, to have the only expression be painful...
While speaking of dictionaries, I'd love to see a "dictionary set" which always does a name/value set regardless of whether the value already exists (the current behavior of erring if a value exists for "add" is just an annoying pain; it has never been useful to me for anything. I've never encountered an associative array with that behavior before) and the ability to do a remove on a key and have it silently succeed if the key doesn't exist. I know I could write my own (and Pixie has done that), but it just rubs that a simple add has to turn into an if-exist-then-delete-before-add, even if buried in my own code. Ok, rant over.
The natural data structure to me for that is a vector of weights (or a set of topic/weight pairs), which expresses in Quest efficiently as a dictionary of doubles. And the generic dictionary supports adding any type as a value, so I was able to use it. But... it doesn't save. So now, since I want this to persist across turns and also be saved, I have to keep the weight vector persistently as a string dictionary, and then convert it back and forth to a numeric one when I use it. It's a pain and who knows what sort of performance problems that will cause if I have to, say, search through several hundred "phrases" to decide which one a character will say.
The bottom line: people will always want what you can provide as far as functionality, especially as Quest apps become more complex, especially if it is a generalzation of an existing feature. I know I would go crazy if I could have arbitrary nesting of dictionaries or lists (i.e. dictionaries whose elements are dictionaries, and lists of lists could support multi-dimensional arrays). As of now, I would have to create objects at each level. Which I can do and have done, but it suppresses the design a bit, to have the only expression be painful...

While speaking of dictionaries, I'd love to see a "dictionary set" which always does a name/value set regardless of whether the value already exists (the current behavior of erring if a value exists for "add" is just an annoying pain; it has never been useful to me for anything. I've never encountered an associative array with that behavior before) and the ability to do a remove on a key and have it silently succeed if the key doesn't exist. I know I could write my own (and Pixie has done that), but it just rubs that a simple add has to turn into an if-exist-then-delete-before-add, even if buried in my own code. Ok, rant over.

Alex
20 Dec 2012, 10:43Good points. #899 is logged for the generic dictionary saver - I will look into making it work with any types that Quest supports, so you'll be able to have dictionaries containing dictionaries and so on. I will look at this for v5.4.
I like the "dictionary set" idea too.
I like the "dictionary set" idea too.

jaynabonne
21 Dec 2012, 11:45Not to flip-flop too much, but I was thinking about this some more, and if I can save you some work... 
Now that we can enumerate the attributes on an object, we can possibly use objects as a more robust (and easier to use!) dictionary. I just realized that instead of a dictionary with doubles, I can use an object with double attributes, and that would save fine, I can enumerate the attributes, and I can much more easily set and retrieve values.
Two questions:
1) Is there a way to remove/delete an attribute from an object?
2) Do you see any performance hit to querying attribute values vs members of a dictionary? (I realize you may not know offhand. If so, don't worry about it. But if you have any definite knowledge that would point to a performance issue, I'd love to know about it.)
Thanks for the discussion.

Now that we can enumerate the attributes on an object, we can possibly use objects as a more robust (and easier to use!) dictionary. I just realized that instead of a dictionary with doubles, I can use an object with double attributes, and that would save fine, I can enumerate the attributes, and I can much more easily set and retrieve values.
Two questions:
1) Is there a way to remove/delete an attribute from an object?
2) Do you see any performance hit to querying attribute values vs members of a dictionary? (I realize you may not know offhand. If so, don't worry about it. But if you have any definite knowledge that would point to a performance issue, I'd love to know about it.)
Thanks for the discussion.

Alex
21 Dec 2012, 14:49To remove an attribute, just set it to null.
A dictionary may be slightly quicker. Attributes are saved in dictionaries, but Quest also has to look through inherited types to see if a value is defined there (although it will only do this if it doesn't find the attribute in the object directly).
A dictionary may be slightly quicker. Attributes are saved in dictionaries, but Quest also has to look through inherited types to see if a value is defined there (although it will only do this if it doesn't find the attribute in the object directly).

jaynabonne
21 Dec 2012, 16:13Great. Thanks!
pzolla
24 Dec 2012, 13:13Can you give a quick example of the structure of a 'double' attribute?

jaynabonne
24 Dec 2012, 14:13If I get your question, a double is just a floating point number, like 1.5, 3.14159, or -42.0. That is in contrast to an int, which is just an integer value (like 1, 3, or -42).
pzolla
24 Dec 2012, 14:33jaynabonne wrote:
Now that we can enumerate the attributes on an object, we can possibly use objects as a more robust (and easier to use!) dictionary. I just realized that instead of a dictionary with doubles, I can use an object with double attributes, and that would save fine, I can enumerate the attributes, and I can much more easily set and retrieve values.
Sorry, I should have quoted the original text. I was trying to understand what you were referring to in this statement. I was thinking you were referring to something called a "double attribute" as opposed to an attribute whose value was a 'double' vs. 'int', 'string' etc.
So you are basically stating that an alternative to a dictionary is to create an object that would act as a surrogate dictionary, set attributes which would act like the index, with the values of the attribute being the equivalent to the values of a dictionary. Correct?
edit: typo

jaynabonne
24 Dec 2012, 15:04Yes, that's what I had in mind. By "double attribute", I just meant an attribute having a double value (or double "type"). The attribute names would be the same as the dictionary keys, and the attribute values would be the corresponding values. The one piece that was missing pre-5.3 was the ability to enumerate the "keys" (attribute names) of an object.
Of course, using the built-in dictioaries are probably easier and more straightforward in general (e.g. you can just assign on to a variable or object attribute by invoking "NewXXXX"). But it is an option if you want something special - for example, a persistent "dictionary" that doesn't fit the string- or object-dictionary form.
Of course, using the built-in dictioaries are probably easier and more straightforward in general (e.g. you can just assign on to a variable or object attribute by invoking "NewXXXX"). But it is an option if you want something special - for example, a persistent "dictionary" that doesn't fit the string- or object-dictionary form.