That
_position_at
method bugs me. I could probably explain it, but I’m not sure anyone else can understand it just by reading it. Can we make it a little better? Let’s find out.
Here’s the method in question:
function FixedIntervalPath:_position_at(distance)
local intervals = distance/self._interval -- float
local interval_index = intervals//1 -- integer
local fractional_interval = intervals - interval_index -- 0 <= f <= 1
local strided_index = (2*interval_index) % (self._length)
return strided_index, fractional_interval
end
The purpose of this method is to convert the input distance desired, into an index into the data array, which has 2 elements per index (stride 2, we’d say in SL).
You know what? I see a better way to write it. Let me do that and then explain the new one. Research pays off. A brief search of the Internet netted me a function I was not aware of math.modf
, which splits a number into its integer and fraction parts. We now have:
function FixedIntervalPath:_position_at(distance)
local integer_part, fraction_part
= math.modf(distance/self._interval)
local strided_index = (2*integer_part) % (self._length)
return strided_index, fraction_part
end
In the original code, I was not clear in my own mind about what I was doing. I was hammering the input values around until I got the numbers that worked, and didn’t realize that all I really needed was the integer and fraction part of the value of distance
over interval
, which you can think of as a real number index into a very fine-grained array. Since our array is actually indexed by the integer part and we interpolate the fraction part, we needed to spit it.
So between “You know what?” and “We now have:” above, i wrote out the integer and fraction bits separately using //1
and %1
which produce the integer part and fraction part, respectively. Then I remembered that Python has something that produces both in one go, and wondered whether Luau has as well. A quick search and there’s math.modf
.
I think that all that’s left to dislike is the magic number 2. We are presently assuming a stride of two. We might, someday in the future, make stride a parameter to the object’s creation, but not today.
We could store a value 2 in self._stride
and use that in the method for greater clarity but I think the variable name should serve well enough.
So. Better? I think so. Worth doing? It is for me: I like learning better ways to do things.
But I would suggest that most anyone can benefit from occasionally improving bits of code that they spot. Better code is easier to understand and therefore easier to get right, easier to spot when wrong. It keeps the code maintainable, reduces errors, is usually smaller and often a bit faster.
Of course it’s important to have tests. The fact that with a single keystroke I could be sure that I hadn’t broken anything was key to my feeling able to do this.
Darn it, I hate it when the right thing to do actually works better than just hacking away joyfully. Don’t you?
Safe paths!