Julia, 995 818 713 613 bytes
g=(s,n)->(w=map;f=t->(t[z=end];for i=1:z-2,j=eachmatch(r"([*o]|(\+)|(\ ))(?(2)-|(?(3)\^|\1))+\1",t[i],1>0),k=i+2:z N=j.match;N[M=end];p=N[1];J=j.offset;u=w(i->i[[J:J+M-1]∩[1:end]],t);try p%3<1?for l=matchall(r"^([*o])\1+\1",u[k]),q=3:endof(l) w(r->r[[1,q]],u[i:k])⊆["$p$p"]&&return(i,k,J,J+q-1)end:u[k]==replace(N,"^","v")&&w(r->r[[1,M]],u[i+1:k-1])⊆[p<33?"<>":"||"]&&return(i,k,J,M+J-1)end;end);o=ones(5)';T=split(s,'\n');(i,j,k,l)=f(T);u=w(collect,T);(a,u[i][r=k+1:l-1],u[j][r],b,c)=41+[2 4 4 83 83;70o;-9 53 77 19 21;o][n,:];u[i][I]=u[j][I=[k,l]]=a;w(e->(e[k]=b;e[l]=c),u[i+1:j-1]);join(w(join,u),'\n'));
Sin disculpas con la explicación:
function g(s,n)
# First, we define function f(t), which finds the box
function f(t)
# determine the number of rows of text
z=length(t)
# Get an iterator of all of the matches to iterate over
# Regex handles all four box styles
temp=i->eachmatch(r"([*o]|(\+)|(\ ))(?(2)-|(?(3)\^|\1))+\1",t[i],1>0)
# Iterate over rows up to third-last one (i)...
# and over any possible box-tops on each of those rows (j)...
# and all possible box-bottom rows for each possible box-top (k)
for i=1:z-2,j=temp(i),k=i+2:z
# N holds the matched box-top
N=j.match
# M stores the length of the match
M=length(N)
# p holds the first letter of the match, the corner character.
p=N[1]
# J holds the position of the first character of the match in row i
J=j.offset
# The intersection here allows truncation of each row to only those
# parts that lie within the valid range of the box-top
u=map(i->i[[J:J+M-1]∩[1:end]],t)
# A try block is being used to skip if a BoundsError is encountered
# this BoundsError will occur if a box cannot be formed due to
# a row not being long enough to form both sides or to form bottom
try
# This distinguishes between simple boxes (types 2 and 4)
# from fancy boxes (types 1 and 3), as code differs between them
if p%3<1 # "then" for simple boxes
# loop over l either doesn't run (if bottom won't form a match
# from position 1 within u) or holds the unique match
# then loop over q looks at all possible bottom-lengths
for l=matchall(r"^([*o])\1+\1",u[k]),q=3:endof(l)
# If box sides are found to match top and bottom...
if map(r->r[[1,q]],u[i:k])⊆["$p$p"]
# return the coordinates of the box
return(i,k,J,J+q-1)
end
end
else # "else" for fancy boxes
# If the bottom matches the top (replace fixes for type 3)...
if u[k]==replace(N,"^","v")
# ... and the edges are also there...
if map(r->r[[1,M]],u[i+1:k-1])⊆[p<33?"<>":"||"]
# return the coordinates
return(i,k,J,M+J-1)
end
end
end
end
end
end
# That defines function f(t), now for the replacement part of the code
# Input s is a single string with newlines, split into separate strings
T=split(s,'\n')
# Find the coordinates of the box using f(T)
(i,j,k,l)=f(T)
# u holds the same strings, but stored as char arrays
u=map(collect,T)
# Here, we have the appropriate replacement characters for each type
# with n determining which character from each array is taken
# Variable names are used here to make it clearer
corners = ['+';'o';' ';'*'][n]
topedge = ['-';'o';'^';'*'][n]
bottomedge=['-';'o';'v';'*'][n]
leftedge = ['|';'o';'<';'*'][n]
rightedge= ['|';'o';'>';'*'][n]
# Assign the appropriate characters in the appropriate places
u[i][[k,l]]=corners
u[j][[k,l]]=corners
u[i][k+1:l-1]=topedge
u[j][k+1:l-1]=bottomedge
# Iteration is required here because it's an array of arrays
for e=i+1:j-1
u[e][k]=leftedge
u[e][l]=rightedge
end
# All that's left to do is recombine to form a single string again
# we join each internal char array into single-line strings...
# then join the strings together with a newline delimiter, and return
return join(map(join,u),'\n')
end
Al contrario de cómo lo abordé por primera vez, este código solo funcionará correctamente para los números de tipo "válidos": 1, 2, 3 o 4. Se divide en dos partes: un buscador de cajas y un reemplazador de cajas. El código del buscador de cajas, función f(t)
, utiliza expresiones regulares para localizar las partes superiores y, para las cajas más simples (tipos 2 y 4), las partes inferiores.
La primera expresión regular es la forma más sencilla que pude encontrar para encontrar las tapas de cajas. Aquí está la lógica:
r"([*o]|(\+)|(\ ))(?(2)-|(?(3)\^|\1))+\1"
([*o]|(\+)|(\ )) < This finds the first corner
( 2) ( 3) . if a + or space, conditionals
( 1 ) . kick in, so they're captured
. separately
(?(2)-| ) < If a +, top edge must be
. at least one -
(?(3)\^|\1) < Otherwise, if a space, top
. edge must be at least one ^,
. otherwise, repeat the corner
+ < Allows more than one top-edge
. character
\1 < finish with the same corner
. char found at the start
El código se usa así:
julia> s="""This is a test document.
It************* has
no *purpose* other than
dem*onstrat*ion.
************o""";
julia> print(s)
This is a test document.
It************* has
no *purpose* other than
dem*onstrat*ion.
************o
julia> print(g(s,1))
This is a test document.
It*+-------+*** has
no |purpose| other than
dem|onstrat|ion.
**+-------+*o
La impresión no está incluida en la función, ya que el formato de salida no está especificado: solo devuelvo la cadena, puede imprimirla después como se ve arriba.