Strings

BlitzMax Forums/BlitzMax Beginners Area/Strings

sswift(Posted 2006) [#1]
It doesn't really seem to be documented anywhere, but apparently you can access strings like this?

A$ = StringName$[0]

To get the first character in the string.

But this does not work!

Test$ = "Blah"
Test[0] = "b"
Print Test$

I get the error "Expression must be a variable".

This seems inconsistent to me. Why do strings seem to be some special case array which I can't write to? I even tried writing ints to it in case attempting to write the string was the issue, but no dice. I wanted an easy way to replace a character at a certain position in a string.


sswift(Posted 2006) [#2]
Well I foound this thread that discusses the problem:
http://www.blitzbasic.com/Community/posts.php?topic=48376

So there's no solution? It seems crazy that we should have to waste tons of time creating and adding slices just to change one character in a string!


sswift(Posted 2006) [#3]
This is weird:

Temp2$ = Word$[Loop]

Apparently, Word$[Loop] does not return a string, it returns an int which represents the character code. So when you do the above, what you get is the character code, converted to a string.

This on the other hand gives you the expected result:
Temp2$ = Chr$(Word$[Loop])


Dreamora(Posted 2006) [#4]
No there is no solution.

And there isn't a real reason you need to use slices and such stuff for that:

Strict
Local test:String
test = "Blah"

test = "b" + Right(test,test.length - 1 )
Print test



There is a large amount of string functions that are especially designed to handle the fact that you can't dynamically modify the content within an existing string. No need to make it complicated or the stone age way of C.


sswift(Posted 2006) [#5]
Lovely:

Test$ = "Blah"
Print Test$[0]

Output:
66

Test$ = "Blah"
Print Test$[1..]

Output:
lah


It's outputting different things depending on whether you ask for a single element or multiple elements!


Dream:
Hahaha! Stone age?! Being able to access individual characters is stone age?

I'd love to see how slow your code is when you need to take a list of 100,000 words and scramble the letters in each using MID$, and then do lookups on that list to find words with specific letters.

Oh sure, I could work AROUND this problem by converting the strings to arrays or banks or something... But how is that BETTER than the "stone age" way of doing things? It's not. It's just more of a pain in the ass.

I'm glad you have an 8ghz machine with 2 gigs of ram, and can use Mid$ and Right$ to do complex operations on large string databases without this being an issue for you!

How is this setup in any way better than C? Safer code?

Yeah, maybe. Until you realise you can't actually DO anything useful using this method without CHEATING, and going right back to the old way of doing things, or rather, even older ways of doing things that are worse than C. Like having to take the extra step of converting all your strings to arrays, and being forced to decide on a max string length as a result and be willing to waste a lot of ram. Or making some kind of an array of arrays which is really just an array of strings with a length byte but now you have to handle all the stuff manually. That's sooo much better.

So tell me again how Right$(Mid$(Len()-Left$())) or whatever you think is so great is NOT complicated, but String[10] = "A" IS?


Dreamora(Posted 2006) [#6]
If you like your 20 year old C++ like hacking on stuff that much, why don't you do us all a favor and just use it?
Its quite annoying ...
You come up each time again with stuff that works like this in C++ and expect that BM behaves as well like an over 20 year old programming language.

Compare to java and C# and you will see that string there behave the same.

If you want to have a string that can be modified insitu, you have to use StringBuffers in Java, so create yourself something similar or ask mark, but don't trash Strings and their functionality just because you lack some knowledge of how "modern" programming languages work and must work.
What you try to achieve with this access is something BM just not allows: Pointer hick-hack! It worked till 1.12 or so, but by then, pointers were removed from BM internal usage due to problems (BM is managed, pointers arent -> serious problem)
Agree with it and life with it.


It might sound harsh, but thats how modern languages work.
But BM is able to interface with stoneage languages, so why not use this fact.

here the way to do it the stone age way:

Strict
Local test:String
test = "Blah"
Local t1:Byte Ptr = test.tocstring()
t1[0] = Asc("b")
test = String.fromCString(t1)
Print test


edit:
As you see, this way would even allow partial replacement through the memcpy commands etc, just in case there are questions on how to do it with more than 1 character :)


sswift(Posted 2006) [#7]
I don't use C++ and I never have.

Java sucks. It is slow and crappy. I have never used a Java app which was not slow, and which did not crash half the time.

If your ideal language is Java, it must only be because it is cross platform capable. And because you are certifiably insane.


If you want to have a string that can be modified insitu, you have to use StringBuffers in Java, so create yourself something similar or ask mark, but don't trash Strings and their functionality just because you lack some knowledge of how "modern" programming languages work and must work.




Must work? Why don't you explain why they must work this way. What is so dangerous and evil about allowing me to specify String[3] = "b" rather than doing all this crap?

Function StrReplaceIndex(SourceStr:String Var, replaceString:String, Index:Int)
	SourceStr = SourceStr[..Index] + replaceString + SourceStr[Index+1..]
End Function



I've been coding in Max for a good five months now and have nearly written a complete game in it. I'm pretty familiar with the OOP features of it by now, AT LEAST THOSE WHICH ARE DOCUMENTED, and I am well aware of the problems and omissions which cause issues like being unable to allow users to make objects that are extensions of my sprite type.

Frankly I'm offended that you think my way of coding is somehow outdated (it serves me quite well thank you very much) and that OOP is perfect in every way.

As for your stone age way with the pointers, that is unacceptable because then I'm mixing and matching code which is automatically managed with code I have to manually free, and that is just a recipe for disaster.

Of course my code already IS a recipe for disaster, because surprise surprise your wonderful Max OOP isn't so perfect after all and almost every type I've made in my game has had to have a Free() function to remove sprites from their internal list, or remove the animation types from their internal list, or remove the collision objects from their internal list, etc, etc etc. And don't even get me started on managing all the list pointers which I have to make sure get cleared so I don't have cyclic references there either.

If rather than insulting my style of code, and shoving in my face how wonderful OOP is and how wrong I am for wanting to do this, you would just tell me that A) You can't do that, and B) WHY you can't do it* then that would be most appreciated. But I don't appreciate being talked down to because I choose to use different methods than you.


(* Saying they must work that way is not an explanation of why they work that way. I was able to accept that 2D arrays aren't realy contiguous bits of memory because Mark explained why. I'm not going to simply accept strings hav to be this way without a good reason. Also I learn nothing about OOP by simply accepting that something has to be some way without knowing why it has to be that way.)


sswift(Posted 2006) [#8]
How is it possible for one person with no apparent track record to wind me up so much? Argh.


Who was John Galt?(Posted 2006) [#9]
Haha don't worry about it Sswift. I still can't figure out how the garbage collector has made life any easier for us Maxers - but it's killed off other functionality.


WendellM(Posted 2006) [#10]
Lovely:

Test$ = "Blah"
Print Test$[0]

Output:
66

Test$ = "Blah"
Print Test$[1..]

Output:
lah

It's outputting different things depending on whether you ask for a single element or multiple elements!

I agree: that surprised me when I first encountered it. To me, only strings should be returned, never integers (that's what Asc() has always been for). It seems like pretty strange behavior for a single-element slice to return integers while a multi-element slice returns a string, but it's intentional - from the help for Asc:

Rem
Asc returns the unicode value of the first character of a string.
End Rem

print Asc("A") '65
Print "A"[0] '65 - equivalent index style implementation


There doesn't seem to be bounds-checking either (even with Debug Build):

Print "ABC"[0] ' gives 65 - OK
Print "ABC"[2] ' gives 67 - OK
Print "ABC"[3] ' gives 0 or 37008 (or other garbage, since it's some random chunk of memory) ??
Print "ABC"[9] ' gives 0 ??
Print "ABC"[-1]' gives 0 ??
Print "ABC"[-2]' gives 3 ??
Print "ABC"[-3]' gives 32767 ??


Cajun17(Posted 2006) [#11]
I would venture a guess that aString[10] = "b" is illegal to make sure people aren't trying to do something like this aString[10] = "blah". I know it easy to say "Duh, one letter per index", but Max has no char type and therefore no way to garuntee that only one character is assigned. What I'd like to know is why can't I do this aStr[10] = 66 to match the read.

Also, I think a StringBuffer object is a good idea.


Dreamora(Posted 2006) [#12]
It would be

(varptr aString)[index] = someCharacter

But this does not work (used to work till pointers were removed). If you try that now with index 0, the whole string is replaced. Other indices don't work.


And the "must work" or ideal have nothing to do with cross plattform or anything but simply with the modern OO design paradigms that came up the last 10-15 years.
Do you think MS created C# just for fun or Apple objective C? Don't think so.
Its only linux that still focus on a "unsecure" language like C/C++ and its pointer stuff, which is highly critical for a stable and secure OS environment.
Managed does not mean its harder. You just need to come away from old behaviors and "ways" of doing things.
After that, when you learned how the GC works and how to use it efficiently, it will normally not take a quarter the time to program new stuff as you won't have bugs everywhere.

*MaxGUI is a good example. Just forget freegadget once on a button that gets reassigned a new button and you will see why this stuff definitely is outdated way of programming - because you will see a button that does not react to your clicks*


sswift(Posted 2006) [#13]
*MaxGUI is a good example. Just forget freegadget once on a button that gets reassigned a new button and you will see why this stuff definitely is outdated way of programming - because you will see a button that does not react to your clicks*


Haha!

Wait a moment. MARK designed MaxGUI.

If ANYONE should know the ins and outs of Max, it's HIM.

So if MARK is stuck using FreeGadget that either says to me that OOP is flawed or Max's implementation of OOP is flawed. And why would Max's implementation of OOP be flawed in some findamental way that Mark is aware of because he encountered it when writing his GUI? You think he would have just made the neccessary changes to fix the problem.

That being the case, it sounds to me like your precious OOP is to blame.

Maybe the reason Mark has a FreeGadget is for the very same reason I have to have a Free function in my Sprite system:


Type Sprite

	Global List:TList
	
	Field Link
	
	Function Create()
		
		Local TS:Sprite
		
		TS = New Sprite
		
		' Store link pointer into list so we don't have to loop through 5000 sprites every time we want to free one.
			Link = List.AddLast(TS)
	
	End Function
	
	Method Free()
	
		Local LoopSprite:Sprite
			
		' Stop all animation of this sprite.
			StopAnimating()
			
		' Stop animation of and free all children of this sprite. (Recursive)
		' (I got lazy here.  I should have kept a list of all children of a sprite with the sprite itself.)
			For LoopSprite = EachIn SpriteList
				If LoopSprite.Parent() = Self Then LoopSprite.Free()
			Next
				
		' Detach this sprite from parent.
			SetParent(Null)			
						
		' Remove sprite's link from sprite list. 
			RemoveLink _Link
			_Link = Null
		
	
	End Method
	
End Type



Now, as you can see there I have my sprites in an internal list. This is so they can be sorted by order, and then looped through.

If you were to just release the sprite by forgetting about it in your own code, it would not be freed, because it is still in the internal sprite list.

There is NO WAY AROUND KEEPING THESE SPRITES IN THE LIST LIKE THIS. THEY HAVE TO BE SORTED BY ORDER BEFORE BEING DRAWN.

But let's assume you have some magical OOP method to solve this particular issue, with weak pointers or something, and as soon as you forget about the sprite, the list forget the sprite existed as well.

Furthermore, even if you solve this problem, you haven't really solved the problem, unless you can also apply that solution to the animation system. Because the animation system will ALSO have a link back to the sprite.

But let's say your animation system also forgets the sprite existed. The pointer to it just goes null. Hey, that's a lot like how pointers worked in Blitz 3D! Sound like a workable solution. All I have to do is check to see if the pointer has gone null and...

Hmm.... I wonder why the behavior changed?

Well MAYBE that is because according to you, that as long as that animation system WANTS to keep animating the sprite, it should be able to. Setting that pointer to null on it suddenly is a no-no right? Or did you just mean feeeing the memory the pointer points to without altering the pointer? If that is the case and setting the pointer to null at all locations (or only specified locations) is okay, then I have to wonder why it hasn't been implemented, because Mark has done it before, and it makes your perfect OOP world with no Free() functions possible.

Of course you still have to set pointers in your code to Null when you want to stop displaying a menu that you have saved a pointer to. Unless you have some magical OOP desing paradaigm that allows to to avoid that by sticking stuff in functions just right so that the pointers lose scope automagically. But I don't have time to design crazy stuff like that which will take 10x as long to code. Would be nice if my sprite system and a lot of my other custom types that need internal lists worked that way though.

Oh and no Sprite.Free() = Sprite which does not respond to your clicks because of that list. Same as MaxGUI.


sswift(Posted 2006) [#14]
Oh and I would just like to point out that half your argument for OOP is that it allows for a secure application.

That may be a noble goal and all, but as a game developer, making my app secure is the last worry on my mind. The first is shipping on time.


sswift(Posted 2006) [#15]

I would venture a guess that aString[10] = "b" is illegal to make sure people aren't trying to do something like this aString[10] = "blah".



That's a silly argument though, because there's no reason Max could not do an internal type conversion from string to char before doing the assignment just as it would if you tried to put an int into a string. So it could crop off any excess quickly and easily.


FlameDuck(Posted 2006) [#16]
Max's implementation of OOP is flawed.
Bingo. Although 'limited' would be a better term.

Maybe the reason Mark has a FreeGadget is for the very same reason I have to have a Free function in my Sprite system:
Yeah. Ghetto coding has a lot to answer for in the realm of superfluous sorce code.

As for the string issue, I'm going to have to side with Mr. Swift, there is no logical explaination as to why he should not be allowed to assign an arbitrary value to a Sting or String slice. As this example demonstrates, Strings are mutable - for the most part.
Local aString:String = "Blah"
Local i:Int
Print Int(Varptr (aString))
aString :+ " Swift"
Print Int(Varptr aString)



marksibly(Posted 2006) [#17]
Max Strings are 'read only', or 'immutable'. This is done simply becomes it makes them very efficient to share - all you have to do is pass a point around to share a string.

To allow stuff like str[0]=65 would mean strings would need to be copied around all the time, or you could potentially mess up someone else's string.

Locking this topic as the tone is getting pretty obnoxious.