New Lua functions, math.sign and math.clamp

Performance wasn’t really a concern because either way is pretty fast. It’s 100% about code readability. I did do some testing, but the difference is really negligible. Using ternary is even a little faster. Sharksie is completely right.

There’s a good reason why people say not to micro-optimize. It’s because your time is better spent finding less complex algorithms. Understanding big-O notation and using it to judge your algorithms is a huge first step!

7 Likes

<3!

In the future, could we possibly have a function called math.movetowards(current, target, maxDelta)?
It would behave like this:

local function moveTowards(current, target, maxDelta)
	if current < target then
		return math.min(target,current + maxDelta)
	elseif current > target then
		return math.max(target,current - maxDelta)
	else
		return target
	end
end

I use this function a lot in my code, mostly in heartbeat threads for doing visual transitions between states.
It would be handy to have something like it built-in.

6 Likes

That, plus the Lua pattern of A and B or C also does not always behave like a ternary, which can be a pitfall if you get comfortable using it with literals and then try to use it to conditionally copy the value of a variable in position B that happens to end up having the value of false or nil, because:

false and false or true -> true     false ? false : true -> true
true and false or true -> true(!)   true ? false : true -> false
false and true or true -> true      false ? true : true -> true
true and true or true -> true       true ? true : true -> true
5 Likes

You mean you don’t want to use the new math.sign() function to rewrite it like this? :laughing:

local function moveTowards(current, target, maxDelta)
	return current + math.sign(target-current) * math.min(maxDelta, math.abs(target-current))
end
5 Likes

New clamp is actually twice as fast as ternary in unprivileged code.
The speed gain is less significant in plugins–blame the mysterious dispatch overhead.

6 Likes

not entirely sure why you would use ternary for clamp. Is it faster than
math.min (max, math.max (x, min))

According to this glorious thread, ternary is faster than min-max.
“Which clamp is faster” is almost never a question that you need to ask, though.

Ayoooo that was my solutionnnn

Awesome. But as always, I’m bad at being grateful for what we have and always want more (what a materialistic world we live in). So I’d really like us to have a math.round function too :slight_smile:

3 Likes

Interesting. :stuck_out_tongue:

Looks good to me. Math.Round would also be very useful.

I think native bit-wise operators could be handy. Particularly for things like custom simplex noise functions.

2 Likes

The C-style ternary is how I expected both to work - why is Lua’s “A and B or C” pattern not functionally equivalent to the ternary operator in C?

Because it’s not a ternary operator. It just happens to somewhat emulate it.

2 Likes

and and or are operations on their own. and comes before or, so a and b or c is the same as (a and b) or c, which might be easier to understand.

2 Likes

Because it’s a pattern people use a lot, since it works most of the time and has the arguments in the same visual order as a C-style ternary, but it’s not the correct logic expression for a 2-to-1 Multiplexer. The more correct logic expression is (A and B) or ((not A) and C) which requires the condition ‘A’ to be written out twice, thus breaking the nice visual similarity with the ? ternary. Also, even this expression won’t let you assign nil, because logic won’t evaluate to nil, only false.

3 Likes

Append “or nil” to turn false into nil.

1 Like

And the expression gets even longer if you want to be able to conditionally return either false or nil. :smile: You have to use true for false, negate it all, and… please no one do this.

1 Like

That expression works fine:

A,B,C = false,1,false
print((A and B) or ((not A) and C)) --> false
A,B,C = false,1,nil
print((A and B) or ((not A) and C)) --> true
-- or "short" notation: A and B or not A and C

Because and just returns the second parameter if the first is truthy and the first paremeter otherwise:

false and 1 --> false
nil and 1 --> nil
true and false --> false
true and nil --> nil