Showing posts with label advent-of-code. Show all posts
Showing posts with label advent-of-code. Show all posts

Friday, December 20, 2024

Getting deep recursion to work in a simple Ruby program on Mac

As part of one of my Advent of Code 2024 solutions, I wrote a small program that included a fairly deep level of recursion. 

When run on my Mac, using Ruby 2.7.2, the program failed to run to completion, generating a stack trace like this (shown in part for brevity):

Traceback (most recent call last):
    3304: from day-16/day-16-part-1-dfs.rb:125:in `<main>'
    3303: from day-16/day-16-part-1-dfs.rb:86:in `explore_map'
    3302: from day-16/day-16-part-1-dfs.rb:86:in `each'
    3301: from day-16/day-16-part-1-dfs.rb:96:in `block in explore_map'
    3300: from day-16/day-16-part-1-dfs.rb:86:in `explore_map'
     ... 3292 levels...
day-16/day-16-part-1-dfs.rb:87:in `key?': stack level too deep (SystemStackError)

I could have fixed this by rewriting my recursive function to use a queue instead, but I wanted to see if I could change my runtime configuration to get my program to run as-is; and I was able to do so! I ended up needing to change TWO things to get the program to run successfully.

First, I needed to increase the system stack size limit for my Mac, for the current terminal session. For my particular program, I needed to set it near my machine's max:

ulimit -s 65520

(The default stack size limit had been 8176.)

Making just that change allowed my program (when run from that terminal session -- not from my IDE) to reach a deeper count of recursions; but it still failed with a similar error:

Traceback (most recent call last):
    10920: from day-16/day-16-part-1-dfs.rb:125:in `<main>'
    10919: from day-16/day-16-part-1-dfs.rb:86:in `explore_map'
    10918: from day-16/day-16-part-1-dfs.rb:86:in `each'
    10917: from day-16/day-16-part-1-dfs.rb:96:in `block in explore_map'
    10916: from day-16/day-16-part-1-dfs.rb:86:in `explore_map'
     ... 10908 levels...

day-16/day-16-part-1-dfs.rb:32:in `hash': stack level too deep (SystemStackError)

Second, I needed to set an environment variable to increase the Ruby VM's maximum allowed stack size. For this "toy" program, I just picked an arbitrary very large value:

export RUBY_THREAD_VM_STACK_SIZE=50000000

With that change also in place, my program was able to successfully run to completion!

I did end up refactoring my solution from using a depth-first search to a breadth-first search, which, in addition to not needing to deal with Ruby's limit on recursion stack depth, was a lot more efficient for solving that particular problem.

I wanted to make a note on what I did, though, so that if I ever face a situation again where I'm bumping up against a Ruby stack depth limit (and a solution other than increasing the limit isn't called for), then I'll be able to remember what I did this last time!

Friday, December 10, 2021

Kotlin: My first impressions, via days 1-3 of Advent of Code 2021

While working on Ruby solutions to this year's Advent of Code programming challenges, my attention was caught by one of the sponsored messages running in the site's sidebar, from JetBrains:

Get ready to jingle with Advent of Code in Kotlin! Have fun, learn new things, and win prizes. Believe in magic with Kotlin. Happy holidays! https://jb.gg/AoC

That link redirected to a post on the JetBrains / Kotlin blog inviting developers to try out the Kotlin language via the Advent of Code (AoC) problems, and providing a nice Kotlin AoC GitHub template as a starting point.

I'd never worked with Kotlin before, but I have used AoC to try out new programming languages before. Serendipitously, I had some time available today, and so I decided to give it a go!

In the time I had, I wound up solving the first 3 days of AoC 2021 in Kotlin.  Here's a quick write-up of my very first impressions of Kotlin!

What I liked!

Good out of the box support in the IntelliJ IDE (which I downloaded for the first time today), including suggestions for more idiomatic Kotlin syntax. (Which makes sense, as JetBrains provides both the IntelliJ IDE and the Kotlin language itself!)  I was saved from some instances of typical "trying to write language X as if it were language Y" newbie mistakes by warnings/suggestions in the IDE.

IntelliJ displays, inline in loop declarations, whether the lower and upper bounds are inclusive of exclusive. This made it easy for me to understand whether, in a Kotlin until loop over an array, I needed to use myArray.length or myArray.length - 1 as the upper bound of the loop. (The former!)

I found the data class syntax to be a nice succinct and readable way for a method to return some related values. In a method in one of my Ruby AoC solutions where I was returning two values just wrapped together in an array, it was easy in the Kotlin equivalent to declare a one-liner data class, and then have my method return an instance of that class, to make what was being returned much more obvious to readers.

If ... else in Kotlin is an expression, not a statement. While looking up an equivalent for the typical ? : ternary expression syntax, I actually enjoyed discovering that ? : is actually not supported in Kotlin, and an inline if ... else should be used instead.

Support for ".."-syntax ranges like in Ruby (e.g. (5..15) to represent the set of integers 5 through 15, inclusive) was fun to see in a C-like language.

The variable-declaration keywords val and var, similar to let and const in JavaScript ES6, to respectively declare immutable and immutable variables.

The ability to pass a function "pointer" as a parameter to a function -- something both the Ruby and Kotlin variations of my Day 3 solution actually made use of.

 

What I maybe didn't like so much

I actually wasn't aware of this ahead of time, but Kotlin is built on top of Java. (Or perhaps it's more accurate to say that Kotlin, like Java, is a language that runs on the JVM?) The first time I became aware of this was when my program threw an exception... and there was a mix of Java and Kotlin code in the call stack. I have no idea to what extent aspects of Java poke their heads up while working in Kotlin... but it's something that I'd want to understand more about before committing to using Kotlin for a real project, versus other available languages where that kind of thing is much more of a non-issue.

JetBrains' Kotlin template for AoC helpfully included a check call to test each problem's provided sample answer against the corresponding sample input. What wasn't so helpful was that a failure of the test just resulted in a generic java.lang.IllegalStateException : Check failed -- with no accompanying mention of the check's expected or actual values. I had to manually add a print statement to find out what the failure was. Maybe I was just missing something?

I ran into some trouble using the built-in pow to raise 2 to the power of a particular variable:

  • It took me a little while to figure out why I was getting an Unresolved reference: pow.  I ended up googling for that exact phrase, while led me to discover that an import kotlin.math.* is needed. Not surprising in retrospect, but it would have been nice to a a prompt in Kotlin's error message about an import/reference possibly being needed. (I believe C# does this, probably among others.)
  • The pow method (as of the current version, 1.6) can't be used on integers. After a little more confusion, I figured out that I had to use the literal 2.0 (instead of just 2), and then manually convert the result back to an Int after getting my result. Why make developers do that?

 

Verdict

Verdict? It's much too early for a verdict! I only worked with Kotlin for a few hours, on some toy problems, on my own! 

It was fun, though, to get to play with a new language! Kudos to the Kotlin team at JetBrains for their combination of the Advent of Code sponsored message, their blog post, and the Kotlin Advent of Code GitHub template, which taken together, were enough to get me to take a look at Kotlin!

And if you're a developer but have never taken a look at Advent of Code before, I strongly recommend checking it out! The event is extremely well crafted and executed each year. It's worth your time!