Retina , 45 43 bytes
A contagem de bytes assume a codificação ISO 8859-1.
O$#`.(?<=(.+))|¶
$.1
!`(?<=(¶)+.*)(?<-1>.)+
O avanço de linha principal é significativo. Entrada e saída são listas terminadas com avanço de linha de seqüências de caracteres ASCII imprimíveis (observe que ambos têm um único avanço de linha final).
Experimente online!
Eu sabia por um tempo que a transposição de blocos retangulares seria uma dor na Retina (enquanto a transposição de quadrados não é tão ruim), mas nunca realmente tentei. A primeira solução foi de fato de 110 bytes, mas após várias mudanças substanciais na abordagem, os 45 bytes resultantes não são tão ruins quanto eu suspeitava (mas ainda assim ...). A explicação seguirá amanhã.
Explicação
Etapa 1: Classificar
O$#`.(?<=(.+))|¶
$.1
Isso faz o trabalho principal de reordenar os caracteres na entrada, mas acaba atrapalhando a separação em linhas. Curiosamente, se removermos o|¶
, obteremos o código necessário para transpor uma entrada quadrada.
Os estágios de classificação (indicados por O
) funcionam assim: eles encontram todas as correspondências da regex especificada (a coisa após a `
) e, em seguida, classificam essas correspondências e as reinserem nos locais onde as correspondências foram encontradas. Por acaso, esse regex corresponde a todos os caracteres: feeds sem linha via .(?<=(.*))
alternativa e feeds de linha via ¶
. Portanto, ele classifica todos os caracteres na entrada. A parte mais interessante é o que são classificados por .
A $
opção ativa um modo "ordenar por", em que cada partida é substituída pelo padrão de substituição na segunda linha, que é usada para comparar as partidas. Além disso, o#
informa ao Retina para converter o resultado da substituição em um número inteiro e comparar esses números inteiros (em vez de tratá-los como seqüências de caracteres).
Então, finalmente, precisamos olhar para o regex e a substituição. Se a primeira alternativa corresponder (ou seja, correspondermos a qualquer caractere dentro de uma das linhas), o grupo (?<=(.*))
capturará tudo até esse caractere nessa linha no grupo 1
. O $.1
padrão de substituição substitui isso pelo comprimento do grupo 1
. Portanto, o primeiro caractere em cada string se torna 1
, o segundo se torna 2
, o terceiro se torna3
e assim por diante. Agora deve ficar claro como isso transpõe uma entrada quadrada: todos os primeiros caracteres das linhas vêm primeiro e todos acabam na linha superior, depois todos os segundos caracteres terminam na segunda linha e assim por diante. Mas para essas entradas retangulares, também estamos combinando os feeds de linha. Desde grupo1
não é usado nesse caso, a substituição está vazia, mas para os fins da #
opção, isso é considerado 0
. Isso significa que todos os feeds de linha são classificados para a frente.
Portanto, agora temos os caracteres na primeira ordem (primeiro caractere de cada string, segundo caractere de cada string, etc.) e todos os feeds de linha no início.
Etapa 2: partida
!`(?<=(¶)+.*)(?<-1>.)+
Agora precisamos dividir os caracteres em linhas do tamanho correto. Esse comprimento corresponde ao número de linhas na entrada original, que corresponde ao número de feeds de linha que temos no início da string.
A divisão é feita aqui com a ajuda de um estágio de correspondência, que simplesmente encontra todas as correspondências do regex especificado e usa a !
opção para imprimir essas correspondências (o padrão seria contá-las). Portanto, o objetivo da regex é corresponder uma linha por vez.
Começamos "contando" o número com o lookback atrás (?<=(¶)*.*)
. Ele gera uma captura em grupo 1
para cada avanço de linha na frente.
Então, para cada uma dessas capturas, combinamos um único caractere com (?<-1>.)+
.