Obscure Code
BlitzMax Forums/BlitzMax Programming/Obscure Code
| ||
Does anyone have any unusual code or obscure ways of writing code that others may find interesting? To start you off, here are some of my favourites: Take the following code: Graphics 800,50 Local X% = GraphicsWidth()/2 Repeat Cls DrawText( "^", x, 20 ) If KeyHit( KEY_LEFT ) Then x:-20 If KeyHit( KEY_RIGHT ) Then x:+20 Flip Until KeyHit( KEY_ESCAPE ) Now delete both IF KEYHIT lines and replace them with one line: Graphics 800,50 Local X% = GraphicsWidth()/2 Repeat Cls DrawText( "^", x, 20 ) x :- ( 20 * KeyHit( KEY_LEFT )) - ( 20 * KeyHit( KEY_RIGHT )) Flip Until KeyHit( KEY_ESCAPE ) How often do you see the following construct in your code, where you perform a short IF-THEN-ELSE: Local A% = 10 Local B% = 11 If A=B Then Print "IT'S TRUE" Else Print "IT'S FALSE" End If This can be shortened to the following: If A=B Then Print "It's True" Else Print "It's False" If you want you can have multiple instructions by seperating them with a semicolon like this: If A=B Then Print "It's True";Print "A" Else Print "It's False";Print "B" How about a large IF-THEN-ELSEIF-THEN-ELSE... Local a%=-1 If a<0 Then Print "ALPHA" ElseIf a=0 Then Print "BETA" Else Print "GAMMA" End If This can be tidied up using a variation of the Select statement Local a%=-1 Select True Case a<0 ; Print "ALPHA" Case a=0 ; Print "BETA" Case a>0 ; Print "GAMMA" End Select However if find the most useful form of this is in a variation of the first example: Graphics 800,50 Local X% = GraphicsWidth()/2 Local str$ = "^" Repeat Cls DrawText( str, x, 20 ) Select True Case KeyHit( KEY_LEFT ) ; X:-20 Case KeyHit( KEY_RIGHT ) ; X:+20 Case KeyHit( KEY_UP ) ; str = "^" Case KeyHit( KEY_DOWN ) ; str = "v" End Select Flip Until KeyHit( KEY_ESCAPE You can Nest Functions inside each other. This makes them only available within the parent function like this: one() Function ONE() two() Function TWO() Print "Hello" End Function End Function But you can also define types within functions : one() Function ONE() Local data:class = New class data.info = 10 two( data ) Function TWO( item:class ) Print item.value() End Function Type class Field info% Method value$() Return info End Method End Type End Function |
| ||
As someone who codes for a living, I avoid a lot of this stuff. In my experience 'clever' coders make a mess for everyone else and often have trouble understanding their own code when they come back to it. Of course, for curiosity or if you're a one man band there's no harm. The two I use regularly that I don't think affect readability are dropping 'then' and immediate use of boolean variables in a conditional without doing a comparison If (boolean_variable) 'code here endif |
| ||
As someone who codes for a living, I avoid a lot of this stuff. Ditto. It is senseless to make your code less readable, just for the sake of being a smart arse. Even if the code is only for your own use, I can guarantee you'll look at it again six months down the line and think "holy ****, why the hell did I do it like that"? |
| ||
If (boolean_variable) 'code here endif If you are also coding in other languages this could be a false friend (things >0 are TRUE, all others are FALSE). For BlitzMax this is no problem, but it could save some trouble as there is no real "bool" type. But thanks for the SELECT-example using the "select TRUE". Never knew this works in BM. bye Ron |
| ||
You can nest functions inside each other Sadly you can't access the locals of the outer function in an inner one. But you can use globals in a function as workaround. Function outerFunc(input:Int) Global globalInput:Int globalInput = input innerFunc Function innerFunc() Print globalInput End Function End Function But you can also define types within functions I didn't know that. Interesting. |
| ||
Only thing that I find hard to read afterwards is when checking against numbers like:if status = 2 Instead I try to use constants to make it more readable like: if status = STATUS_READY -Henri |
| ||
Dare I say that functions/types within functions is a side effect of the parser/compiler and 'shouldn't really' be allowed. Although to the contrary, I have used functions within functions, and it's actually quite handy - kind of like local functions. On topic... I guess the only 'handy' thing not mentioned already that I can think of right now is a simple switch between true and false. Instead of using If BoolFlag = True BoolFlag = False Else BoolFlag = True Endif you could use BoolFlag = True - BoolFlag Not much else to add really, other than agreeing that these sorts of things go against creating good readable code, and also a +1 to Henri for mentioning to use constant names and not to use immediate values. I suppose it's that old saying - 'just because you can do something, it doesn't mean you should' EDIT:- You can pretty much put a functions and types anywhere a statement is expected, definitely not documented : Strict Local Condition = True If Condition Type TestType Field Pointless Method Create:TestType(Value) Pointless = Value Return Self EndMethod EndType Function Foobar() Local Test:TestType = New TestType.Create(10) Print Test.Pointless EndFunction Foobar EndIf |
| ||
This is mostly for curiosity, although I do find I use the SELECT TRUE! @Col: You can also do this: BoolFlag = not BoolFlag How about adding to an array without resizing it first: Local options$[] = [ "RED", "GREEN", "BLUE" ] options :+ [ "YELLOW" ] For Local n% = 0 To options.length-1 Print options[n] Next |
| ||
That resizing is useful, I personally hate local array:int[] = [] ... array = array[..array.length+1] array[array.length-1] = newEntry Now I know that "array = array + [newEntry]" should be the same :D bye Ron |
| ||
Just reminded myself of this this afternoon so thought I'll stick it here... Renaming existing functions to your own versions and you can still call the original using module scope:- Strict Function Print(t,g) BRL.StandardIO.Print "Using the original Print function via a local Print function: "+t+","+g EndFunction Print 10,20 Recommend against it though :) |
| ||
That last one has a straightforward use if you need to use modules from two different authors, who might have done something like that accidentally (or perhaps two different versions of the same code, or...). |
| ||
I didn't know the nested functions bit. That's actually pretty handy. That way you can define mini programs inside of your program. Neat! |
| ||
That array thing is really neat, didnt know you could do that. One thing i end up doing now and then to reduce (readable) code size is something like if a>b then count:+1 endif to directly using that evaluation like count:+(a>b) |
| ||
Just come across this little nugget... Say you have an object with a method name that you later realise has the same name as a function somewhere in your code ( or anywhere within the 'Max language ). If you are in say another method of that Type and want to call the named method its not a problem as 'Max will always look to call the more local version ( ie the method ), but what if you want to call the function version. BRL already thought of it and you can use a period in front of the call so that you can call the function version and not the more local method version. As always an example helps... Strict Type MyType Method PrintSomething() Print "MyType.PrintSomething" EndMethod Method UseSelfPrintSomething() PrintSomething ' call the MyType version of PrintSomething EndMethod Method UseFunctionPrintSomething() .PrintSomething ' call the function version of PrintSomething EndMethod EndType Function PrintSomething() Print "Function PrintSomething" EndFunction Local Test:MyType = New MyType Test.UseSelfPrintSomething Test.UseFunctionPrintSomething |
| ||
@dave. "." just calls the "global" in all cases. global var:int type mytype field var:int End Type is behaving the exact same way. So prepend a "dot" if accessing something outside the type (a global), "super" when accessing a parent, "self" or nothing when accessing an instances properties. Think this isn't "obscure code" - just something not very well documented :D PS: I am now a regular user of adding to arrays: array :+ [entry] Thanks. bye Ron |
| ||
Instead of using clever constructs I use lots of simple functions with rational interfaces. That way my main code is short but readable and understandable at least by me. An example is toggle, to switch a Boolean state as above: My num = toggle(mynum) Function Toggle:int(num:int) Return (num=0) End function I think this is the shortest way of doing things especially in the long run where you gain over time by building up your library of small functions. For a lot of things like this, look into the language Forth which is a stack based language for embedded systems where concise is king. The Forth compiler source code is full of clever little tricks like this. Another is Perl, especially for any sort of string manipulation. The functions themselves may not be simple but it has a wealth of very handy tool functions that can make the actual program you are writing very short and powerful. |
| ||
that toggle is a bad example as "state = 1 - state" does the same. Also keep in mind that Blitzmax does not optimize things: so you have a function call each time as an overhead. bye Ron |
| ||
Note that `1 - state` only works if `state` is guaranteed to be 1 or 0. Since BlitzMax allows unrestricted ints as boolean values this isn't always the case, although I should hope anyone writing code knows when it isn't guaranteed to be the case. (Any real-world code that takes advantage of False and non-False to do something practical would be a solid contender for this topic.) That said, you could just replace Toggle with Not. To the experienced programmer there's a good chance it would be more readable, too. |
| ||
Yeah you are absolutely right, but beginners also think that "int = null" is something different than "int = 0" (no real "null" for simple non-objects like integers, strings etc.). Instead of "1 - state" you can also write "1 - (state > 0)" because that evaluation is possible in BlitzMax. bye Ron |
| ||
for a simpe toggle of boolean values there is also state= not state which is my favorite. |
| ||
I don't see how that toggle thing would actually work correctly although, admitedly, I've not actually tried it yet. I've always used a true/false flag for toggles such as: If UserHasQuit=True Exit Else do something else End If Then again i really like to make stuff readable by someone that isn't me so don't mind doing it with a few more lines. |
| ||
I wasn't aware that Blitzmax didn't optimize. I'll certainly keep that in mind. I programmed many years ago back when function overhead was a HUGE problem-- it makes it rather difficult to discern what I should be optimizing and in what ways. Based on that information there are a lot of things in my codebase that I could probably unroll and inline... |
| ||
Better let one with "knowledge" support my argument but I am quite sure to have read multiple times that function calls are expensive in BlitzMax - because of no compilation step optimizing them out. This would then especially be problematic if you use many "Getters" (but...they allow overwriting behaviour which normal properties do not allow per se). bye Ron |
| ||
Speaking relatively, function calls may be more expensive in BlitzMax than in other languages because there is something where perhaps in C there would be nothing... ...but on 21st century hardware you can hardly say a function call is "expensive". They're very close to free nowadays to a predictable target (polymorphic methods or changing function pointers may incur a small hit, but you obviously can't inline those anyway). Most of the places you'd care about this tiny overhead are the sort of software you probably wouldn't use Max for anyway, e.g. the core of a high-performance renderer or physics engine... Max is for using those things. |
| ||
That is why I use Getters ... because the bottleneck is the gpu-part (no auto batching, fbo ...), not the cpu-part. The ability to overwrite/extend types overwhelms the disadvantages. With another compiler (gcc) this could get auto-optimized out so maybe in the future that situation might cure itself. bye Ron |
| ||
Did you know you can get the decimal part of a number by using MOD 1? |
| ||
Back to the original question. Like John Galt I avoid clever code whenever possible since most of what I write is for a production environment. Clever is only a consideration for speed or bandwidth. |
| ||
But knowing these things and understanding why they work makes you a better programmer. The thing with Blitzmax is that it hasn't been picked over to nearly the same extent that C has so there are going to be plenty of interesting nooks and crannies that nobody has found yet. |
| ||
Did you know you can get the decimal part of a number by using MOD 1? Made an interesting discovery. Normally I would use num - Int(num) as Mod would tend to be slower. After I did some tests, I found that Mod is slower in debug, but faster in Release. |
| ||
> But knowing these things and understanding why they work makes you a better programmer Having seen what some of the people who "know these things" come up with I can emphatically say that is false. |
| ||
Having seen what some of the people who "know these things" come up with I can emphatically say that is false. Ignorance is strength. Part of "knowing these things" is knowing when not to use them. Look at code by Donald Knuth sometime. The guy is basically an Einstein-level genius of deep magic code but what he writes is typically very readable and clear... Except when it isn't, and that isn't the fault of the guy who has been programming since 1960. ["readable and clear" refers to program code and not his written english.] There's also such a thing as pursuing knowledge for its own sake, which is unfortunately something not everybody understands. |