Hello all!
I have been searching the internet for a better solution to this, since I now my way around Nim, but I am looking for ways to write better/smarter/faster code.
I have a total of 256 procedures, basically all doing the same:
So 256 procedures with a title which is unique, but the rest is predictable. Having 1000+ lines of code to do just this seems tedious. I thought of creating a dictionary,
{{key_unknown : 0}, {key_soft_up : 1}, .. },
and use that whenever I want to call one of the procedures.
What would be a smart way to clean up this code?
This being Nim you can of course do the same at compile time. One possible way to generate all those procedures from definitions like (key_unknown, 0) etc. would be:
import macros
proc function1(s: string): string = discard
proc function2(s: string): string = discard
proc function3(s: string): string = discard
proc genProc(name: NimNode, arg: int): NimNode =
let bodyStr = "some_argument " & $arg
result = quote do:
proc `name`*(): string {.discardable.} =
`bodyStr`.function3().function2().function1()
macro genProcs(procTab: untyped): untyped =
result = newStmtList()
for arg in procTab:
doAssert arg.kind == nnkTupleConstr
doAssert arg[1].kind == nnkIntLit
result.add genProc(arg[0], arg[1].intVal.int)
echo result.repr
genProcs:
(key_unknown, 0)
(key_soft_up, 1)
key_unknown()
Of course, this is only a "dummy" similar to the OPs example.
This would get you down from O(1000) lines to 256 lines + the macro code.
Thanks for the suggestions!
@shirleyqurik, I created the crosspost: https://stackoverflow.com/questions/72679549/how-could-i-optimize-this-nim-code-with-lots-of-similar-functions
@cblake, unfortunately it doesn't always have a 'key' part, see the link above I renamed one of the proc's to make this a bit more clear. If I understood your answer correctly, I'd still need a dictionary like {{key_unknown : 0}, {key_soft_up : 1}, .. }, (or something similar) to feed into this template to create the 256 functions no? That would be no problem, but I am trying to be sure before posting any attempt to solve this.
My answer sentence was about if the "key" part also varied. For example, if you also had "value" and "keyVal" and "valKey" as a 4x multiplier to get to 256 with 64 whole suffixes. That sounds like a lot, though. You didn't really elaborate enough on how things get to 256. 256 is not divisible by 3. So, it cannot quite be what you said. Maybe that key/prefix part is what you mean by a "title"? And your StackOverflow link now says it is deleted. :-(
I think you will get better help here by being more specific.
StackOverflow told me to post the question at Code Review, see the updated url :-) https://codereview.stackexchange.com/questions/277484/how-could-i-optimize-this-nim-code-with-lots-of-similar-functions)
256 is just a coincidence, to give more context:
So this list goes on and one. The only variable parts are the title of the proc's and the value after the 'some_argument', but these just count from 0 - 255.
Since as you say the value in the "some_argument" string only increments, you could of course not hand that integer explicitly (as I do in that genProcs macro), but rather just count the arguments.
And if you really want to go for conciseness in terms of lines of code, the above macro could simply receive an array of identifiers / static strings (which would have to be in order then of course!).
In any case, as the naming of the procedures essentially is "random", you of course won't get around to writing them out somewhere in the code once.
As the modification to the above snippet is so small for a sorted array input, here's that code for reference:
import macros
proc function1(s: string): string = discard
proc function2(s: string): string = discard
proc function3(s: string): string = discard
proc genProc(name: NimNode, arg: int): NimNode =
let bodyStr = "some_argument " & $arg
result = quote do:
proc `name`*(): string {.discardable.} =
`bodyStr`.function3().function2().function1()
macro genProcs(procNames: untyped): untyped =
result = newStmtList()
doAssert procNames.kind == nnkBracket # make sure argument is an array
for idx, arg in procNames:
doAssert arg.kind == nnkIdent
result.add genProc(arg, idx)
echo result.repr
genProcs([key_unknown, key_soft_up, key_soft_down])
key_unknown()
Perfect, thanks!
This is what I ended up using.