This function finds the 8 char passcode from a given "doorId".
Is there a way to find the passcode in an undetermined number of steps without using a while
? Maybe involving streams? val passcode = stream(logic).take(8)
.
def findPassCode(doorId: String): String = {
var passcode = ""
var count = 0
while (passcode.length < 8) {
val hash = md5(doorId + count)
if (hash.startsWith("00000")) passcode = passcode + hash.drop(5).take(1)
count = count + 1
}
passcode
}
-
4\$\begingroup\$ This code appears to be for 2016 Advent of Code, Day 5. It would be nice if you would include such contextual information in the question. \$\endgroup\$200_success– 200_success2016年12月06日 05:29:55 +00:00Commented Dec 6, 2016 at 5:29
1 Answer 1
First of all, you have the passcode
length hard-coded. It would be better to make it a parameter, perhaps with a default value. The same goes for the string the "interesting" hashes start with ("00000"
).
Also, there are several ways you could make the code more functional. Here are a couple ideas:
- Use recursion: Computing an \$n\$ character pascode is accomplished by first computing an \$n-1\$ character passcode, then computing the next character. Or, you could rewrite the loop in a tail-recursive form.
- You could use streams. Streams are Scala's version of a lazy list / sequence.
Using streams, we can use the following logic:
- Generate a stream of consecutive
Int
s, starting at0
. - Combine each number in with
doorId
- Compute the hash of each combined string
- Filter the hashes, keeping only hashes that start with five
'0'
s - Take the sixth character of each hash
- Take the first
8 - doorId.length
characters, and append them todoorId
We can express this in code using something like:
def findPassCode(doorId: String): String = {
val chars = Stream.from(0) // Step 1
.map(n => doorId + n) // Step 2
.map(md5) // Step 3
.filter(h => h.startsWith("00000")) // Step 4
.map(h => h(5)) // Step 5
doorId + chars.take(8 - doorId.length).mkString // Step 6
}
-
\$\begingroup\$ Thank you, really helpful! I always want to take 8 characters from the stream. Thats the nature of the passcode. So what I want is simply
doorId + chars.take(8).mkString
. Your code worked without other modifications! Really happy to get rid of the loop and conditionals. They are festering grounds of bugs. \$\endgroup\$Red Mercury– Red Mercury2016年12月07日 14:42:46 +00:00Commented Dec 7, 2016 at 14:42 -
\$\begingroup\$ @RedMercury, ah yes, I think I misunderstood exactly what the
passcode
was supposed to be slightly. Glad you found it useful, though. You're right about looping (and explicit recursion) -- it should be avoided if possible. I agree that hard-coding for 8 characters is ok for a simple exercise. But remember people tend to do as they practice -- so it's better to always practice best practices (at least to a point). \$\endgroup\$Nathan Davis– Nathan Davis2016年12月07日 17:14:42 +00:00Commented Dec 7, 2016 at 17:14 -
\$\begingroup\$ The implementation could be simplified further as
Stream.from(0).map(n => md5(doorId + n)).filter(_.startsWith("00000")).map(_(5)).take(8).mkString
. \$\endgroup\$200_success– 200_success2016年12月08日 07:17:22 +00:00Commented Dec 8, 2016 at 7:17 -
\$\begingroup\$ I found that the functional solution using a
Stream
, though more elegant, performed much poorer than the original code. \$\endgroup\$200_success– 200_success2016年12月08日 07:18:14 +00:00Commented Dec 8, 2016 at 7:18