I'm a complete beginner in Nim, and I've been trying to learn the basics by solving Advent of Code tasks.
I have previous Python experience and my Python solutions for AoC were my guidelines for solving these tasks.
The things I like in Nim:
The thing I noticed - in tasks Day 07 and Day 09, where I use nre module, the speedup compared to Python is much smaller than usual. Is this the expected behaviour? Or can I do something to improve Nim speed there?
But the main reason why I'm posting this (after only 10 tasks) is because I would really like, before I continue with these tasks, if somebody more experienced would take some time to go through my solutions and to correct/improve my Nim.
If something can be done more idiomatically, if I should use different data types, if I didn't use some useful module/function, if there's a better and/or faster way to do some things - please let me know.
Thank you in advance!
I've added day 12 solution, and updated some earlier tasks.
Any comments on the coding style? Something I could improve?
P.S. Why is this thread visible only if I'm logged in?
Probably it was on moderation. Now it's visible.
Yeah, it was fixed in the mean time.
Any comments/tips on the code maybe? :)
I took a look at your day 7 code, and it looks good. The reason that it's only 2x as fast as python is because nre is allocation heavy: it can't know what you're planning to do with your RegexMatch objects, so it must allocate a new one each call.
However, split() and findAll() fully control the lifetime of the RegexMatch object, so it would be possible to optimize these two methods to reuse the buffer. This optimization would make day7.nim about 1.5 times faster, or about 8.5 times faster than the equivalent python program.
I don't have time to perform this optimization myself, but if anyone wants to take a shot at it, I'd be happy to review their code and help with getting it merged :)
Thanks for taking the time to analyze the problem!
I was surprised what were you talking about, until I realized these are my solutions for AoC 2016 (not 2017) :)
In the mean time, my Nim knowledge has improved — here are my AoC 2017 solutions, if anybody wants to take a look. Comments and critique are very welcome!
I generally prefer keeping the scope of local variables as small as possible, e.x. on 2017_05, I would prefer
func stepByStep(originalInstructions: seq[int], secondPart = false): uint32 =
var
instructions = originalInstructions
line: int = 0
while line < instructions.len:
let jump = instructions[line]
if secondPart and jump >= 3:
dec instructions[line]
else:
inc instructions[line]
line += jump
inc result
A lot of times I think this is a significant readability improvement: fewer lines have the opportunity to read/write that variable, so it's easier to reason about. In this case the improvement is minimal though.
On 2017_15, the following expression is used in the Nim code: ((a xor b) and 0xFFFF) == 0. Some people might be thrown off by the use of the xor, so I think it could be further improved by doing a and 0xFFFF == b and 0xFFFF. I would expect a Sufficiently Smart Compiler to optimize this the same way as the previous statement. And in fact, my compiler performs exactly that optimization (rdx is a, rsi is b, rdi is result):
xor %rdx,%rsi
movzwl %si,%esi
cmp $0x1,%rsi
adc $0x0,%rdi
2017_22 is less clear. Part 1 seems to represent infected as 1 and clean as 0, so to improve readability, you could rename status to isInfected and represent it as a bool. I can't understand what part 2 does--what do the 0..4 represent, and what does the number it calculates mean?
Anyway, nice work on those examples. I generally found them nice to read, and the table with the problem and links to the code was really helpful.
I generally prefer keeping the scope of local variables as small as possible
I used to do that, but at some point I decided to declare the variables outside of the loops. I thought it might be a bit faster, but it is probably not noticeable in these examples, and I guess I overdid it.
((a xor b) and 0xFFFF) == 0. Some people might be thrown off by the use of the xor, so I think it could be further improved by doing a and 0xFFFF == b and 0xFFFF.
That was the original version, and I've changed it to the "more cryptic" one as an attempt to make the code faster. day15.nim is the slowest of all 25 tasks and I've tried to do as much as possible to make it faster. If the compiler optimizes it anyway, I might return to the more readable version.
I can't understand what part 2 does--what do the 0..4 represent, and what does the number it calculates mean?
This is from the second part of the task (you can't see it until you solve the first part):
Now, before it infects a clean node, it will weaken it to disable your defenses. If it encounters an infected node, it will instead flag the node to be cleaned in the future. So:
* Clean nodes become weakened.
* Weakened nodes become infected.
* Infected nodes become flagged.
* Flagged nodes become clean.
I guess I could, instead of numbers, use enum in both parts to make the code more readable/understandable.