Doing something stupid
|
Eloise Pasteur
Curious Individual
Join date: 14 Jul 2004
Posts: 1,952
|
11-27-2005 08:17
I'm trying to trim a float down so it loses the trailing 0's and if it's an integer the decimal point too. This is the function I've written: string rTrim(string show) { if(llGetSubString(show, -1, -1)=="0") { show=llDeleteSubString(show, -1, -1); rTrim(show); } else if(llGetSubString(show, -1, -1)==".") { show=llDeleteSubString(show, -1, -1); } llOwnerSay("Final return is "+show); return show; }
If I give it 0.50000 I get the following output: Object: Final return is 0.5 Object: Final return is 0.5 Object: Final return is 0.50 Object: Final return is 0.500 Object: Final return is 0.5000 Object: Final return is 0.50000 Object: Display is 0.50000 and for 2.00000 I get: Object: Final return is 2 Object: Final return is 2. Object: Final return is 2.0 Object: Final return is 2.00 Object: Final return is 2.000 Object: Final return is 2.0000 Object: Final return is 2.00000 Object: Display is 2.00000 I'm obviously doing something stupid, but can anyone tell me what?
|
Yumi Murakami
DoIt!AttachTheEarOfACat!
Join date: 27 Sep 2005
Posts: 6,860
|
11-27-2005 08:49
From: Eloise Pasteur
I'm obviously doing something stupid, but can anyone tell me what?
Without analysing it too much - when you make the recursive call to rTrim, you're not storing the return value in a variable. Thus, any changes done by the recursive call are being lost. Change line 6 to show = rTrim(show)
|
Eloise Pasteur
Curious Individual
Join date: 14 Jul 2004
Posts: 1,952
|
11-27-2005 10:45
D'Oh. Thanks.
|
Ziggy Puff
Registered User
Join date: 15 Jul 2005
Posts: 1,143
|
11-27-2005 12:56
I thought I'd throw in my two cents here. Unbounded recursion in a low-resource environment like LSL isn't a very good idea, IMO. If you start with a 'real' float then you only have 6 or 7 digits after the decimal point, so that's probably OK. But in general, with a scheme like this, you don't have any control over the depth of recursion. Which means the potential for a stack-heap collision error, if someone calls this with a string that has too many digits after the decimal point. In other words, this code can be broken with the right (wrong?) input.
There are several ways around this. You could easily re-write this to be a loop - while the last character is 0 or a decimal point, remove that character. It might look less cool from a programming point of view, but it'll be much more predictable in terms of memory usage, and may even run faster. If you want to use recursion, you could add a second parameter to the function that keps track of depth (with each level, increment that value), and if you detect that the recursion stack has grown too deep, abort with an error message.
None of this will make any difference with a script that's this simple, but it's worth keeping in mind when you try to do the same thing in a much larger script where you're low on memory.
|
Eloise Pasteur
Curious Individual
Join date: 14 Jul 2004
Posts: 1,952
|
11-27-2005 16:16
This particular one can only have floats as an entry, so I'm happy with it. I've got a similar for left trimming strings of white space that potentially runs 255 levels of recursion and I don't get stack heap collisions with it even when I feed it the 255 space characters - I've just tested it.
I'm not sure how LSL handles recursive calls, but they don't seem as memory hogging as you'd expect.
|
Huns Valen
Don't PM me here.
Join date: 3 May 2003
Posts: 2,749
|
11-27-2005 16:25
This is my (non-recursive) method. If the float has nothing but zeros after the decimal, the decimal is removed as well. string trimFloat(float f) { string theFloat = (string)f; integer j = llStringLength(theFloat); integer i = j - 1; integer trimZeroes; integer done = FALSE; string char; while(!done && i > 0) { char = llGetSubString(theFloat, i, i); if(char == "." || char != "0") { done = TRUE; } else { i--; trimZeroes++; } } theFloat = llGetSubString(theFloat, 0, j - trimZeroes - 1); if(llGetSubString(theFloat, -1, -1) == ".") theFloat = llGetSubString(theFloat, 0, llStringLength(theFloat) - 2); return theFloat; }
|
Ziggy Puff
Registered User
Join date: 15 Jul 2005
Posts: 1,143
|
11-27-2005 23:23
OK  I just thought I'd mention some alternatives.
|
Eloise Pasteur
Curious Individual
Join date: 14 Jul 2004
Posts: 1,952
|
11-28-2005 01:15
Sorry Ziggy, I've just reread my post and I was rather tired when I wrote it. I think it comes over rather more aggresively than I meant it when I wrote it.
The reason I did the recursive call was because the lTrim function that I'd done before worked perfectly well with recursion up to 255 levels. When I wrote it I didn't stop and think about it's impact on memory, although if I'd had stack-heap collisions you can be sure I would have done.
It does seem rather odd that something that's so delicate about memory allows this level of recursion, I agree, and it makes me wonder why and how these things are handled in the virtual machine, whilst (at the same time) acknowledging that I probably won't understand the answer - I'm a reasonable scripter but not a computer scientist by any stretch of the imagination.
|
Ziggy Puff
Registered User
Join date: 15 Jul 2005
Posts: 1,143
|
11-28-2005 08:20
Oh, I didn't think your response was aggressive at all  No offense taken. The size of a stack entry depends on the local variables declared in that function and the parameters passed into the function (among other things). So, for instance, if you've got a list variable that is passed into the function, and let's say each recursive level adds a couple of new entries and then returns the result - something like that will use up more memory, because a new copy of the list is being made on the stack for every function call. Do the same thing, but make the list a global variable, and now only one copy exists, on the heap, and you've just cut your memory use by several kilobytes. So that's another trick that can be used. In general globals are a bad ideas when local variables will work, but in a low resource environment, sometimes using a global for situations like this is the better design. IMO anyway 
|
Eloise Pasteur
Curious Individual
Join date: 14 Jul 2004
Posts: 1,952
|
11-28-2005 09:11
Glad to hear there was no offence.
Thank you for the explanation. Looking back I'm pretty sure I was using a global string for the one with 255 levels of recursion, can't get back in world to check at the moment but I seem to remember it was something I needed to access from a couple of events for different reasons, hence a global variable. That might be what saved it I guess.
Strangely for lists I never programme recursively, the for loops and while loops seem to do lists better to me, at least when it comes to debugging. Perhaps that's because if I'm stripping stuff out of a list I tend to do more other things to it than stripping off leading white space or trailing 0's so I could easily get lost in the recursion but a for loop 'reads' OK, whilst a relatively simple function like this I think of recursively (do this and do it again until we run out of 0's) so I script it that way.
lol, this is running the risk of becoming a psychology of scripting thought, I'll stop now! Thanks again for the help and the explanation.
|