r=reverse
m(o:c:k)=o:c:c:c:k++" "
f 1=[["\\ /","__ "],["/__\\"]]
f n|[w:i,j]<-map m<$>f(n-1),_:_:k<-r$m w=[r k:w:i,k:j]
h[i,j]=unlines$r<$>r i++j
h.f
Try it online!
A more recursive version of this answer. The arbitrariness of this challenge makes this pretty frustrating.
Explanation
This answer is a bit hard to explain. The challenge is, as I already said, arbitrary in a few ways, so the code is sort of just a nest of symbols.
Idea
The idea of the program here is to build up the two halves. That is when calculating the nth hexagon we get the two halves for the n-1th hexagon and use that to make the next biggest one.
There are some caveats though. We build the top half up-side-down and we build both halves mirrored left to right. We do this because it is convenient to do it this way. No deep reason it just makes things shorter even if it does make things a little incomprehensible.
Details
The first line is pretty straight forward r
is an alias for reverse
. The second line is not so straight forward. m
is a nonsense function, it exists because it or a similar operation needs to be done in a few places. It doesn't really have a semantic meaning. The best explanation of what it does here is the code.
m(o:c:k)=o:c:c:c:k++" "
From here we start getting to f
which handles basically all of the logic. The first case for f
is the base case, it is pretty standard
f 1=[["\\ /","__ "],["/__\\"]]
Note that we return a list of two items instead of a tuple. In any sane program we would be using a tuple since it is fixed at 2 elements. However later we will map over both arguments of this with the same function. It is hard to do that with a tuple but easy with a list, and the list doesn't pose any drawbacks so we use it.
Then we have the inductive case. First we fetch the previous case, and double map our m
over it. This makes the hexagon 1 unit wider (2 characters) and moves it half a unit (1 character) to the right (although since this whole thing is backwards the space characters are added on the right). We pattern match this to [w:i,j]
because we want to use w
to make new rows later. Speaking of which next we make the rows. We do this with a pattern match:
_:_:k<-r$m w
This is sort of nonsense code. It just slaps together things we already had to produce the correct output. k
and its reverse form the new rows so we add them in. and return that.
After f
we have h
which turns the output of f
into a string. It undoes all the wacky transforms we used during the construction and packages it up to be used.
With all that we just compose f
and h
for the final function.