Ordenar los libros de texto

31

Ordenar los libros de texto

La escuela comienza pronto (si aún no lo ha hecho) y es hora de poner en orden nuestros libros de texto. Necesita ordenar sus libros en orden alfabético, pero eso lleva demasiado tiempo, por lo que decide escribir un programa para hacerlo.

Ejemplos

Entrada:

 _
| |  _
|F| | |
|o|_|P|
|o|B|P|
| |a|C|
| |r|G|
|_|_|_|

Salida:

   _
  | |_
  |F| | 
 _|o|P|
|B|o|P|
|a| |C|
|r| |G|
|_|_|_|

Entrada

La entrada será un conjunto de libros que deben reorganizarse alfabéticamente. Contendrá solamente: |, _, , y A-Za-z. Los títulos de los libros se leen verticalmente, de arriba abajo.

Puede optar por asumir que la entrada se rellena con espacios en blanco para ajustarse a un rectángulo. Si elige que su entrada se rellene con espacios en blanco, especifíquelo en su respuesta.

La altura máxima de libro que su programa necesitará manejar es de 5,120 líneas de alto sin fallar.

Los libros siempre tendrán 1 grosor y siempre tendrán al menos un libro en la entrada

Salida

La salida deberá ser el mismo conjunto de libros organizados en orden alfabético. La altura de los libros debe permanecer igual y el título debe tener el mismo espacio desde la parte superior cuando se reorganiza.

Los libros deben estar ordenados alfabéticamente. Si su idioma tiene una función de clasificación, puede usarla. De lo contrario, puede usar la clasificación alfabética como se describe aquí .

Ejemplos de títulos de libros

 _
| |
| |
|F|
|o|
|o|
| |
| |
|B|
|a|
|r|
| |
| |
|_|

El título de este libro es:

"Foo  Bar"

Los títulos de los libros solo contendrán letras y espacios.

Se permite el espacio en blanco al final


Victorioso

Este es el por lo que el código más corto en bytes gana.

Downgoat
fuente
¿Hay un límite para la "altura" de los libros?
The_Basset_Hound
@BassetHound No, actualmente no existe, pero no debe preocuparse por apoyar libros de 2 ^ 64-1 de altura. Pondré un máximo en 5,120 "alto" es lo que su programa necesita manejar sin fallar
Downgoat
Muy bien, genial.
The_Basset_Hound
@ETHproductions Sí, los títulos de los libros solo contendrán letras y espacios
Downgoat
1
¿Qué pasa con el grosor de los libros? Siempre 1 columna?
coredump

Respuestas:

5

CJam, 60 bytes

qN/:Kz1>2%{_{" _"-}#>}$_{_'_#>,}%2,\*2ew{:e>('|*K,Se[}%.\zN*

Intenté portar mi respuesta de Python, que también es similar al enfoque de @ RetoKoradi .

Pruébalo en línea . La entrada debe rellenarse con espacios para formar un rectángulo.

Sp3000
fuente
7

Python 3, 231 bytes

def f(s):
 *M,L=sorted(["".join(c).strip()for c in zip(*s.split("\n"))][1::2],key=lambda x:x[1:-1].strip()),;l=m=0
 for r in L+[""]:n=len(r);M+="|"*~-max(n,l),r;m=max(n,m);l=n
 for r in zip(*[x.rjust(m)for x in M]):print(*r,sep="")

Solo un truco rápido. Comprime los libros, ordena, vuelve a cerrar, cuidando las columnas |mientras estamos en ello.

Ingrese una cadena multilínea, rellenada con espacios finales a un rectángulo. La salida tiene un espacio final más en cada línea de lo necesario.

Sin golf

def f(s):
  new_cols = []

  # Zip columns, removing the spaces above each book
  # [1::2] is to skip columns of |s, keeping only the books
  books = ["".join(c).strip() for c in zip(*s.split("\n"))][1::2]

  # Sort based on title, [1:-1] to remove the top and bottom _s
  books.sort(key=lambda x:x[1:-1].strip())

  last = 0
  max_height = 0

  for book in (books + [""]):
    height = len(book)

    # Append |s as necessary for the left edge of the current book
    # The +[""] above is for the right edge of the last book
    new_cols.extend(["|"*(max(height, last) - 1), book])

    max_height = max(height, max_height)
    last = height

  # Rezip columns, add back spaces as necessary and print
  for col in zip(*[x.rjust(max_height) for x in new_cols]):
      print("".join(col))
Sp3000
fuente
Me encantaría ver una versión sin golf, si es posible, por favor.
Pureferret
1
@Pureferret Agregó una versión sin golfing con algunos comentarios
Sp3000
6

Rubí209 204 204 200 198 bytes)

a=n.tr(?|,' ').split$/
i=!p;t=a.map(&:chars).transpose.map(&:join).select{i^=a}.sort_by{|s|s[/[A-Z]/][0]}
x=0;t.map{|t|y=0;u=p;t.chars{|c|u&&a[y][x,3]=?|*3;a[y][x+1]=c;y+=1;u|=c=='_'};x+=2}
a.join$/

los transpose función en esta solución requiere que todas las líneas tengan la misma longitud, por lo tanto, la entrada debe rellenarse con espacios en blanco.

Explicación

def sort_books(n)
  a = n.tr(?|,' ')  # pre-emptively remove all the '|'.
    .split $/         # and split into an array of lines
                      # ($/ is the INPUT_RECORD_SEPARATOR, typically "\n")
                      # we're going to write our answer into `a` later

  i = !p # i = true; we'll use this as a flip-flop variable
         # Kernel#p returns nil with no args

  # we're now going to get a sorted array of book titles (t)
  t = a.map(&:chars)  # break array into nested array of every character
       .transpose     # and transpose the entire array
       .map(&:join)   # this gives us an array of "horizontal" book titles with dividers

       .select { i ^= a } # select every second line
                          # (i.e. just titles without dividers)
                          # `i` starts off true
                          # `a` is truish (it's our original array)
                          # `^=` is the bitwise xor assignment,
                          #      it will alternate true/false on each execution

       .sort_by { |s| s[/[A-Z]/][0] } # sort by the first alphabetical char

  # use counters for less chars than `each_with_index`
  # x and y are cartesian coordinates in the final array

  x = 0 # start in the left-hand column

  # go through each title
  t.map { |t|
    y = 0 # each book title starts on the top row

    u = p # `u` is "have we reached the book's spine yet?" (or are we above it?)
          # `u` starts off false and we'll set it true when we see the first '_'
          # after which we'll start writing the book's edges

    # go through each character of each title, including leading spaces and '_'s
    # this will "descend" down the array writing each letter of the title
    # along with the "edges"
    t.chars { |c|

      u &&                  # if we're on the spine
        a[y][x,3] = ?|*3;   # write ||| in the next 3 columns
                            # the middle | will be overwriten by the title char

      a[y][x+1] = c; # write the current title char into the second (x+1) column

      y+=1; # descend to the next row

      u |= c == '_' # Since '_' is the top and bottom of the book,
                    # this toggles whether we're on the spine
    }
    x += 2 # jump to the right 2 columns and start on the next title
  }
  a.join $/ # hopefully this is obvious
end
Daniel Fone
fuente
¿Qué rubyversión se requiere? Con 2.1.2 para la entrada de muestra de la pregunta me sale "'transposición': el tamaño del elemento difiere (6 debería ser 2) (IndexError)".
manatwork
@manatwork lo siento, debería haber especificado que la función requiere un rectángulo rellenado por espacios en blanco. Actualizaré la respuesta.
Daniel Fone
1
Oh. En efecto. Lo siento, no lo analicé exhaustivamente. Tampoco hoy, así que solo menciono gsub(?|,' ')tr(?|,' ').
manatwork
5

Python 2 - 399 bytes

Espera que la entrada no tenga una nueva línea final.

import sys;a=str.strip;L=list(sys.stdin);b=len(L[-1])/2;s=['']*b
for l in L:
    i=0
    for c in l[1:-1:2]:s[i]+=c;i+=1
s=sorted([a(a(x),'_')for x in s],key=a);y=map(len,s);m=[y[0]]+[max(y[i],y[i+1])for i in range(b-1)]
for i in range(max(y)+1):
    h=max(y)-i;l='';j=0
    for x in s:l+='|'if h<m[j]else' ';l+='_' if h==len(x)else' 'if h>len(x)else x[-h-1];j+=1
    print l+('|'if h<y[-1]else' ')
print'|_'*b+'|'
Tyilo
fuente
5

CJam, 75 66 65 bytes

qN/z(;2%{_{" _"#W=}#>}$es:P;_W>+{_'_#_Pe<)S*2$,'|*.e<@@:P;}%);zN*

Esto espera entradas rellenadas con espacios para formar un rectángulo.

Pruébalo en línea

Gracias a @ Sp3000 y @Dennis por las sugerencias sobre el recorte de cadenas en el chat, así como por darme una pista de que el $ operador puede tomar un bloqueo como argumento.

Todavía no estoy del todo contento con el segundo bucle. Pero después de probar algunas otras opciones sin mayor éxito, me estoy cansando.

Explicación:

qN/     Read input and split at newlines.
z       Transpose to turn columns into lines.
(;      Drop first line...
2%      ... and every second line after that, to keep only lines with titles.
{       Start block that maps lines for sort.
  _       Copy.
  {       Start block for matching first title letter.
    " _"#   Search for character in " _".
    W=      True if not found.
  }#      End match block. This gets position of first character not in " _".
  >       Trim leading spaces and '_.
}$      End of sort block. Lines are now sorted alphabetically by title.
es:P;   Store large number in P. P holds previous position of '_ in following loop.
_W>+    Repeat last title line, so that final separator line is generated.
{       Loop over title lines.
  _'_#    Find position of '_.
  _       Copy position. Will store it in P after the minimum has been determined.
  P       Get position of '_ in previous line.
  e<)     Take the smaller of the two '_ positions, and decrement.
  S*      Generate leading spaces from the count.
  2$,     Get length of title line.
  '|*     Generate full line length sequence of '|.
  .e<     Overlap spaces with '| to give the final separator.
  @@      Get '_ position to top, and stack in order for next loop iteration.
  :P;     Store '_ position in P.
}%      End of loop over lines.
);      Remove last line, which was a repeat.
z       Transpose to turn lines into columns again.
N*      Join with newline characters.
Reto Koradi
fuente
1

Scala 359 341 bytes

espera que todas las líneas tengan la misma longitud (es decir, rellenadas con espacios)

(s:String)=>{def f(s:String)=(" "/:s)((r,c)=>if(r.last=='|'||c=='_')r+"|"else r+" ").init;val h=s.lines.toSeq.transpose.collect{case s if s.exists(_.isLetter)=>s.mkString}.sortBy(_.filter(!_.isWhitespace));((Seq(f(h(0)))/:h.sliding(2))((s,l)=>s:+l(0):+f(l.minBy(_.indexOf('_')))):+h.last:+f(h.last)).transpose.map(_.mkString).mkString("\n")}

sin golf y comentado:

//anonymous method that takes the books ascii-art string
(s: String) => {

  //method to convert the middle to a border
  def f(s: String) =
    //fold (starting from non empty string since we use `.last`)
    (" "/:s)((r,c) =>
      if(r.last=='|'||c=='_')r+"|"
      else r+" "
    ).init.tail

  //h is a sequence of strings of the middle of the books
  val h =
    //transpose lines of input string, and take only the lines the contains letters (middle of the books)
    s.lines.toSeq.transpose.collect{
      case s if s.exists(_.isLetter) =>
        s.mkString
    }.sortBy(_.filter(!_.isWhitespace)) //sort the books by title (actually by "_$title" since we filter out just whitspaces)

  //fold over pairs of books and add the last manually
  (
    (Seq(f(h(0)))/:h.sliding(2))((s,l) =>
      s :+ l(0) :+ f(l.minBy(_.indexOf('_'))) //convert higher book to border and append to folded accumulator
    ) :+ h.last :+ f(h.last) //add last book manually
  ).transpose.map(_.mkString).mkString("\n") //transpose back and construct the output string
}
gilad hoch
fuente