J, 54 bytes
[:<:@#[:".@":@(,`(+:@[,}.@])@.({.@]=[))/^:a:@".@":_,|.
Try it online!
Not my best golf by any means. Surely there has to be a better way of converting a list with one item to an atom.
Explanation
crush =. ,`(+:@[ , }.@])@.({.@] = [)/
times =. <:@# [: ".@":@crush^:a:@".@": _ , |.
crush
This crushes an array once. It needs to be given the array in reverse since J's insert works right-to-left (something I learned today). This doesn't particularly matter, since all we need to output is the number of times we can crush the array.
,`(+:@[ , }.@])@.({.@] = [)/
/ Fold/reduce from the right
{.@] = [ Head of the running array equals the left argument?
+:@[ , If so, prepend double the argument to
}.@] the array minus its head
, Else, prepend the left argument.
times
This is fairly straightforward: apply crush to the array until our result converges, but there are a few issues I had to deal with that result in much more code than I anticipated.
First, when crushing reduces to a single element, that element is actually in a one item list (i.e. it is nonatomic), so the function is applied again resulting in overcounting. To fix this, I used a hack I came up with to reduce a single element list to an atom which is ".@":
(convert to string and then evaluate).
Second, crush
errors on the empty list. I think you can define how a function should behave on receiving empty input with insert (/
), but I couldn't find anything after a cursory look, so I'm using another workaround. This workaround is to prepend _
(infinity) to the list since it will never affect the number of times the array is crushed (_ > 2^64
). However, this results in a single element list consisting of _
when given the empty list, so we need to convert to an atom again before crushing.
<:@# [: ".@":@crush^:a:@".@": _ , |.
|. Reverse input
_ , Prepend infinity
".@": Convert single-element list to atom
crush Crush the list and after
".@": Convert single-element list to atom
^:a: until it converges, storing each
iteration in an array
<:@# Length of the resulting list minus 1
[1,1,2,4,8]
1または4を返す必要がありますか?