Obscure Code

BlitzMax Forums/BlitzMax Programming/Obscure Code

Scaremonger(Posted 2014) [#1]
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



Who was John Galt?(Posted 2014) [#2]
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



GfK(Posted 2014) [#3]
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"?


Derron(Posted 2014) [#4]
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


UNZ(Posted 2014) [#5]
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.


Henri(Posted 2014) [#6]
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


col(Posted 2014) [#7]
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



Scaremonger(Posted 2014) [#8]
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



Derron(Posted 2014) [#9]
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


col(Posted 2014) [#10]
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 :)


Yasha(Posted 2014) [#11]
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...).


Mr. Goober(Posted 2014) [#12]
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!


zzz(Posted 2014) [#13]
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)



col(Posted 2014) [#14]
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



Derron(Posted 2014) [#15]
@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


zoqfotpik(Posted 2014) [#16]
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.


Derron(Posted 2014) [#17]
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


Yasha(Posted 2014) [#18]
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.


Derron(Posted 2014) [#19]
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


UNZ(Posted 2014) [#20]
for a simpe toggle of boolean values there is also
state= not state
which is my favorite.


Xerra(Posted 2014) [#21]
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.


zoqfotpik(Posted 2014) [#22]
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...


Derron(Posted 2014) [#23]
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


Yasha(Posted 2014) [#24]
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.


Derron(Posted 2014) [#25]
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


Scaremonger(Posted 2014) [#26]
Did you know you can get the decimal part of a number by using MOD 1?




dynaman(Posted 2014) [#27]
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.


zoqfotpik(Posted 2014) [#28]
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.


TomToad(Posted 2014) [#29]
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.


dynaman(Posted 2014) [#30]
> 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.


zoqfotpik(Posted 2014) [#31]
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.