15 Jul, 2009, Scandum wrote in the 1st comment:
Votes: 0
I've been working on nested array support for tintin++ and ran into some minor design issues.

Right now #var {bla[x][y][z]} {hello world} creates an associative array.

Now I thought it'd be cool if numbers could be used as well, so $bla[x][y][z] could also be written as $bla[1][1][1] -> assuming x y and z are the first elements. That is:

#var {bla[x][y][y]} {hello world y}
#var {bla[x][y][z]} {hello world z}

Now $bla[1][1][1] would be the equivalent of $bla[x][y][y] and $bla[1][1][2] the equivalent of $bla[x][y][z]

The problem is that I also want people to use numbers, so one could enter: #var bli[22] hello world, but to access this you'd need to use: $bli[1] rather than $bla[22] (which would look for the 22nd element) which is rather unintuitive.

One possible solution I was thinking of is having people use + for indexing, so bli[+1] is used for the first element, and bli[-1] would be used for the last element. This assuming negative numbers are rarely used in associative arrays.

Is there a better solution for this problem?
15 Jul, 2009, David Haley wrote in the 2nd comment:
Votes: 0
I think it would help if you gave a little more background on how your language works, for example if x/y/z are variables or literal strings or …
Also, what exactly you want to happen. It appears that you want to assign an ordering to associative arrays based on the order in which things were added to them.

Anyhow, if you want to index into the array by alphanumeric strings and by positions, and you don't want alternate functions to access the array by position, you don't really have much of a choice. The +/- syntax looks as reasonable as any others.

One question you might want to think about is why you really need to access an associative array by position. The whole point of a map is to access things by keys, not by index. What's your use case here?
15 Jul, 2009, Scandum wrote in the 3rd comment:
Votes: 0
The scripting language is typeless, so x y z are keys. To use a variable you need to prefix it with a $, like in php. To assign a variable you need to use the #variable command which can be abbreviated as #var.

The associative arrays (guess I should call them maps?) are sorted numerically for numbers and alphabetically for non numbers, so they're sorted for all intense and purposes.

The usecase is mostly optimization since using an index is O(1) and a key is O(LogN). I'm not overly familiar with the usage of maps, so I'm not sure if people can go without arrays entirely. If array-less programming is an option that'd be something worth considering, especially if reserving +1 and -1 is going to end up being a pain in the ass.

It'd be nice though if users can use:

#var bla[$bla[-1] + 1] {append this} to append to the end of a numeric map. $bla[-1] would print the value of the index though, and not the index. Not sure if there's a commonly used way to retrieve an index of a map?

Another option is using bla[1] for arrays and bla(bli) for maps, though I guess in that case I should use whatever the standard is, if there is one.
15 Jul, 2009, David Haley wrote in the 4th comment:
Votes: 0
Is there a reason why you can't just separate indexed arrays and associative arrays? It seems like you're seeing a lot of headaches from treating them the same way, but in most use cases, you want either one or the other.
15 Jul, 2009, Scandum wrote in the 5th comment:
Votes: 0
David Haley said:
Is there a reason why you can't just separate indexed arrays and associative arrays?

Mainly to keep the user and programming interface simple.

David Haley said:
It seems like you're seeing a lot of headaches from treating them the same way, but in most use cases, you want either one or the other.

That's what using [] for arrays and () for maps would do, but I'm not sure what commonly accepted standards exist for these kind of things.

Also, I don't like having to allocate and initialize index 1 to 999 if someone wants to set bla[1000], especially since the data structures are pretty large. So it's mainly an interface issue.
15 Jul, 2009, David Haley wrote in the 6th comment:
Votes: 0
It depends on the language. Tcl separates numerically-indexed arrays from associative arrays, and you interact with them quite differently.

You might want to look into what Lua does. It only has one "table" object, and you can use it as an array or a map. In general, note that arrays are just maps whose keys are contiguous integers. When Lua sees that you're not using the table as an array, it switches to hash mode; otherwise, it uses a more efficient representation.
(start edit)
I meant to add that having an array with a big hole in it, like your a[1000] case, makes Lua switch to table mode to avoid allocating a bunch of empty spaces.
(end edit)

It's possible that you're worrying too much about efficiency. If you implement your map as a hash table, you'll get average-case better than log N performance. And I don't think that people are likely to be writing terribly performance-sensitive code in tt++'s scripting language. So even log N would be perfectly fine.
15 Jul, 2009, Scandum wrote in the 7th comment:
Votes: 0
Hrm, that's a solution as well. I could keep a counter for each non numeric key, and if that counter is 0, check if the last item equals the map size.

Another issue is retrieving the index. I think I can have people use &bla[bli] to retrieve the index of the key bli, rather than the value. How does Lua go about this? I guess I'll have to sit down and read up on Lua as well given its syntax seems to be quite popular for mud clients nowadays.

I'm using binary searches, but performance isn't that big of a concern given most muds have a 0.25 second heartbeat, but it makes for a good promotion to claim blazing speeds.
15 Jul, 2009, David Haley wrote in the 8th comment:
Votes: 0
Quote
Another issue is retrieving the index. I think I can have people use &bla[bli] to retrieve the index of the key bli, rather than the value. How does Lua go about this? I guess I'll have to sit down and read up on Lua as well given its syntax seems to be quite popular for mud clients nowadays.

I'm not sure I understand your question? If you mean just how you get values, you write something like this:
a = {} – create a table
a['hello'] = 1 – map string "hello" to 1
print(a['hello']) – print the value of the mapping of 'hello' in 'a'
a[2] = 123 – map int 2 to 123
print(a[2])


Did I misunderstand your question?

Quote
I'm using binary searches, but performance isn't that big of a concern given most muds have a 0.25 second heartbeat, but it makes for a good promotion to claim blazing speeds.

Yes, I suppose so. Although having an easy-to-use scripting language is probably worth more. :tongue:
15 Jul, 2009, Runter wrote in the 9th comment:
Votes: 0
David Haley said:
Yes, I suppose so. Although having an easy-to-use scripting language is probably worth more. :tongue:


Truth.
15 Jul, 2009, Scandum wrote in the 10th comment:
Votes: 0
David Haley said:
Did I misunderstand your question?

I think so. With the current setup you can reference a mapping by both key and index. So #var foo[bar] hello world, could also be referenced as foo[1] if it was the first item in the map.

Then &foo[bar] would report 1 and $foo[bar] would report hello world. Big question is if such a thing would be useful, though it'd allow: #var $array[&array[-1] + 1] {append item}.

People probably expect to use $array += {append item} but tintin has no concept of + and += since '$var += 1' would be seen as a command to be sent to the mud. Not sure if there's a commonly accepted way to handle this, not sure if using #exec foo += 1, is the right way to go about it.

David Haley said:
Yes, I suppose so. Although having an easy-to-use scripting language is probably worth more. :tongue:

Non programmers generally view tintin and derived languages like zscript as easy to use. In tintin you use #delay {1d5+10} {say hi} which can be quite problematic in other clients.
15 Jul, 2009, elanthis wrote in the 11th comment:
Votes: 0
I think a lot of other languages will fail to help you out as guides, because most treat keys either as numeric indexes or as associate indexes. Values don't have both an associative and numeric index.

That said, both PHP and JavaScript technically do have that, although they don't expose it quite so nicely. PHP and JavaScript use O(1) access for associative keys (they use hashes), but they _also_ keep all keys in ordered first-come-first-index order. You can't just ask for the index of a specific key, but if you loop over the values in the array/object, you get them in the order they were added, even though the key lookup mechanism keeps them in some other order (essentially random order for hashes).

The easiest way to implement that is to keep two data structures inside of your script language's array. First you have an array of aggregate key/value pairs (where the key is null/empty for values inserted using indexes). The second then is a tree or hash table that maps keys to indexes. That will make indexed access about as fast as you can get and give you O(1) or O(logN) lookup performance for associative keys (depending on how you implement it). The disadvantage to that scheme is that if you insert/delete indexed values anyway other than the end of the array, you will need to do a bunch of key lookups to update the key->index mapping. You could have the indexed array and map portions store references to key/index/value objects and get O(1) performance (using hash tables) for both indexed and associative key lookup and updates, but I imagine the extra indirect reference in the array won't sit well with your if you're going for Maximum Array Speed.

That said, I'm not sure what your use case is for looking up the index of an associate key. It is a unique feature, but I can't for the life of me think of any practical use for it that couldn't be solved some other way. I dare say that both Lua's and JavaScript's approach is considered "very easy" by non-programmers (Lua was designed specifically for non-programmers, and is very popular today for use in games by game artists and level designers who have no programming background at all) and they both lack the feature you're asking about, and I've never known anybody to ask for it. Are you sure you need the feature at all?

If you just want append, why not an #append command? Or do what PHP does, where an assignment to an array with no index given ($array[] = $foo) equates to append? That's way, way, way simpler to learn and use than $array[&array[-1] + 1] will ever be. One of my complaints with Lua actually is the way you have to use an overly complex append mechanism (table[#table+1] = foo) instead of something simple like what PHP uses or what most OO languages use.
15 Jul, 2009, David Haley wrote in the 12th comment:
Votes: 0
elanthis said:
That said, I'm not sure what your use case is for looking up the index of an associate key. It is a unique feature, but I can't for the life of me think of any practical use for it that couldn't be solved some other way. I dare say that both Lua's and JavaScript's approach is considered "very easy" by non-programmers (Lua was designed specifically for non-programmers, and is very popular today for use in games by game artists and level designers who have no programming background at all) and they both lack the feature you're asking about, and I've never known anybody to ask for it. Are you sure you need the feature at all?

Agreed – as I said I think the main question is whether you really actually need this.
And yes, the append case can be solved with an extra, fairly simple question.

It sounds like trying to maintain both gets really complicated in a single data structure, when the use case for indexes of associative entries is unclear.

One reason to have indexes could be to iterate over the map (for i = 1 … len(map): do something with map), but again you can do that with separate structures/commands (for item in iter(map)).

I guess I'm not entirely clear on what the bigger context is, because I'm not entirely aware of how people typically write scripts in languages like this. I personally very strongly dislike home-grown languages that seem easy at first, but make it very complicated to do things that aren't very simple. Languages like Lua do away with complex syntax and so are very easy for beginners, while allowing experts to do their own thing. But that's neither here nor there, for now…
15 Jul, 2009, Scandum wrote in the 13th comment:
Votes: 0
Utilizing foo[] is an interesting idea.

#var foo[] {add to end}
#unvar foo[] {remove from end}

I think zmud added an #add and #subtract command, but I'd prefer support for /= %= &= and != while at it.

One thing that might work is changing the two argument: #math {var} {expression} syntax to also support a one argument '#math {var = expression}' operation.

Other than the single use case I mentioned I can't readily think of anything, though it would allow easily deleting from the end and from the beginning of a mapping using +1 and -1 which could be useful in some algorithms. It's my experience people don't miss features until they've actually used them, and hence wouldn't typically ask for it. I've grown quite fond of my 'd' operator in tintin for dice rolls for example and I find myself annoyed when it's not available in other languages.

Another issue is what to do in this scenario:

#var bla[x][y] hello
#var bla[x][x] echo
#var bla[x] world

Right now $bla[x] will print {x}{y} so #var bla[x] world is useless, though I guess I should add the option to use: #var bla[x] {a {hi}}{b}{c}

David Haley said:
I personally very strongly dislike home-grown languages that seem easy at first, but make it very complicated to do things that aren't very simple.

It'd be nice if tintin had started out with something decent so I wouldn't have to deal with this mess and focus on actual client development. I probably ought to do something with Lua one day, though in that case it'd be easier to start fresh with no backward compatibility.
19 Jul, 2009, Scandum wrote in the 14th comment:
Votes: 0
So having the above mostly implemented I'm busy with statements.

So far I have:
#break
#case {value} {true body}
#continue
#default {body}
#else
#elseif {expression} {true body}
#if {expression} {true body}
#for {{min} {variable} {max}} {body}
#return {value}
#switch {expression} {body}
#while {expression} {body}

Planning to add foreach, not planning to add do and goto.

Does anyone know of particularly cool statements in other languages that I should look at?
0.0/14