El sorprendente sistema de clasificación de Crazy Librarian

21

¡Es la temporada de regreso a la escuela! Entonces, para un trabajo de medio tiempo, estás ayudando en la biblioteca de la escuela. El problema es que el bibliotecario jefe nunca ha escuchado las palabras "Dewey Decimal", y mucho menos implementó ese sistema. En cambio, el sistema de clasificación en uso ha crecido "orgánicamente" a medida que la biblioteca se ha expandido ...

En un esfuerzo por mantener la cordura, ha elegido escribir un programa para ayudarlo a clasificar los libros a medida que se devuelven, porque ¡ay de usted si clasifica los libros mal! (El bibliotecario jefe es MUY estricto).

De entrada y salida

  • La entrada será una lista de títulos de libros (hipotéticos), uno por línea, de STDIN / idioma equivalente.
  • Puede suponer no más de 100 libros ingresados ​​a la vez (solo puede llevar tantos por la biblioteca a la vez).
  • Los libros pueden tener varias palabras en sus títulos, y estas palabras pueden estar separadas por espacios u otros signos de puntuación (por ejemplo, dos puntos :, un guión).- , etc.).
  • Para facilitar el cálculo, suponga que todos los títulos son UTF-8.

La salida es los mismos títulos, ordenados de acuerdo con las siguientes reglas, nuevamente una por línea, a STDOUT / idioma equivalente.

Las reglas de clasificación

Los libros se ordenan numéricamente según su valor de carácter promedio (es decir, el valor de carácter acumulativo divide el número de caracteres en el título del libro), contados por las siguientes reglas:

  • Todos los caracteres cuentan para determinar el número de caracteres en un título.
  • Las letras minúsculas se cuentan por su posición en el alfabeto. (a = 1, b = 2, ... z = 26)
  • Si el título contiene letras mayúsculas, esas cuentan para 1.5 su valor en minúsculas (A = 1.5, B = 3, ... Z = 39). ("¡Las letras mayúsculas son importantes!", Dice el bibliotecario).
  • Cada signo de puntuación / símbolo en esta lista !@#$%^&*()-=_+[]\{}|;':",./<>?~cuenta -1 desde el valor acumulado antes del promedio. ("¡Los títulos grandiosos no lo son!")
  • Si el título contiene un número, escrito en números arábigos , ese número se resta del valor promedio antes de ordenar. Múltiples dígitos consecutivos se tratan como un número (por ejemplo, 42restarían 42, no restarían 4 y luego restarían 2). Los dígitos individuales no cuentan para el valor acumulado (es decir, cada dígito contribuye con 0), pero sí cuentan para el número de caracteres. Tenga en cuenta que esto puede resultar en un valor negativo y debe tratarse adecuadamente. (Se rumorea que el bibliotecario ha estado enamorado de un instructor de matemáticas durante varios años, ahora).
  • Si el título contiene dos palabras separadas que comienzan con un R, el libro obtiene una puntuación de "infinito" y se arroja a una pila en la esquina (es decir, ordenado al azar al final de la lista). (El bibliotecario fue abandonado una vez por una persona con esas iniciales, o eso has escuchado).
  • Los espacios no cuentan para el valor de carácter acumulativo (es decir, contribuyen con 0), pero contribuyen con el número de caracteres en un título.
  • Caracteres que no se ajustan a las reglas anteriores (por ejemplo, un ÿ ) no cuentan para el valor de carácter acumulativo (es decir, contribuyen con 0), pero sí contribuyen al número de caracteres en un título.
  • Por ejemplo, un libro hipotético ÿÿÿÿÿtendría una "puntuación" de (0+0+0+0+0) / 5 = 0, pero un libro hipotético ÿÿyÿÿtendría una "puntuación" de(0+0+25+0+0) / 5 = 5 .
  • Dos libros que "puntúan" de la misma manera pueden aparecer en su orden de elección. (Están en el mismo estante, de todos modos)

Entrada de ejemplo 1

War and Peace
Reading Rainbow: The Best Unicorn Ever
Maus
Home for a Bunny

Ejemplo de salida 1 (con "puntajes" entre paréntesis para mostrar el razonamiento; no es necesario imprimirlos)

War and Peace (8.5)
Home for a Bunny (10.125)
Maus (15.125)
Reading Rainbow: The Best Unicorn Ever (infinity)

Ejemplo de entrada 2

Matthew
Mark
Luke
John
Revelations

Ejemplo de salida 2 (con "puntajes" entre paréntesis para mostrar el razonamiento; no es necesario imprimirlos)

Mark (12.375)
John (13)
Revelations (13.545454...)
Luke (13.75)
Matthew (~13.786)

Entrada de ejemplo 3

42
9 Kings
1:8
7th

Ejemplo de salida 3 (con "puntajes" entre paréntesis para mostrar el razonamiento; no es necesario imprimirlos)

42 (-42)
1:8 (-9.3333...)
9 Kings (~0.36)
7th (2.3333...)

Otras restricciones

  • Este es Code-Golf, porque necesita mantener el programa en secreto de los ojos siempre observadores del bibliotecario, y cuanto más pequeño sea el programa, más fácil será ocultarlo.
  • Se aplican restricciones de escapatoria estándar
  • No dejes que el bibliotecario te pille holgazaneando gastando todo tu tiempo en PPCG.
AdmBorkBork
fuente
¿Qué pasa si dos libros obtienen exactamente el mismo puntaje? es decir, tengo Reading Rainbow y Ruby Rails
Kishan Kumar
@KishanKumar En esa instancia específica, "dispuestos aleatoriamente al final de la lista" ya que ambos son doble R. En otras palabras, elige tu opción. En el caso general, si dos palabras tienen el mismo puntaje, pueden aparecer en cualquier orden entre sí. Agregaré una viñeta para aclarar eso.
AdmBorkBork
77
Necesita una palabra A para que su sistema tenga un acrónimo. Recomiendo el increíble sistema de clasificación de Crazy Librarian: D
Geobits
3
@Geobits ¿Tienes CLASE?
AdmBorkBork
Los números son solo números decimales? ¿Qué pasa si hay varios, se restan todos por separado?
Paŭlo Ebermann

Respuestas:

5

APL (132)

{⎕ML←3⋄⍵[⍋{2='R'+.=↑¨⍵⊂⍨⍵≠' ':!99⋄↑(+/⍎¨'0',⍵⊂⍨⍵∊⎕D)-⍨((+/∊1.5 1×(⍳×∊⍨)∘⍵¨G)-+/⍵∊(⎕UCS 32+⍳94)~'`',⎕D,∊G←(⊂⎕A),⊂⎕UCS 96+⍳26)÷⍴⍵}¨⍵]}

Como todos los demás están haciendo lo mismo, esta también es una función que toma una serie de títulos y los devuelve ordenados, por ejemplo:

      titles
┌─────────────┬──────────────────────────────────────┬────┬────────────────┬───────┬────┬────┬────┬───────────┬──┬───────┬───┬───┐
│War and Peace│Reading Rainbow: The Best Unicorn Ever│Maus│Home for a Bunny│Matthew│Mark│Luke│John│Revelations│42│9 Kings│1:8│7th│
└─────────────┴──────────────────────────────────────┴────┴────────────────┴───────┴────┴────┴────┴───────────┴──┴───────┴───┴───┘

      {⎕ML←3⋄⍵[⍋{2='R'+.=↑¨⍵⊂⍨⍵≠' ':!99⋄↑(+/⍎¨'0',⍵⊂⍨⍵∊⎕D)-⍨((+/∊1.5 1×(⍳×∊⍨)∘⍵¨G)-+/⍵∊(⎕UCS 32+⍳94)~'`',⎕D,∊G←(⊂⎕A),⊂⎕UCS 96+⍳26)÷⍴⍵}¨⍵]}titles
┌──┬───┬───────┬───┬─────────────┬────────────────┬────┬────┬───────────┬────┬───────┬────┬──────────────────────────────────────┐
│42│1:8│9 Kings│7th│War and Peace│Home for a Bunny│Mark│John│Revelations│Luke│Matthew│Maus│Reading Rainbow: The Best Unicorn Ever│
└──┴───┴───────┴───┴─────────────┴────────────────┴────┴────┴───────────┴────┴───────┴────┴──────────────────────────────────────┘

Explicación:

  • ⎕ML←3: establecido ⎕MLen 3(para )
  • ⍵[⍋{... }¨⍵]: ordena la entrada por los valores devueltos por la función interna
    • ↑¨⍵⊂⍨⍵≠' ': obtener el primer carácter de cada palabra
    • 2='R'+.=: ver si dos de estos son 'R'.
    • :!99: si es así, devuelve 99! (≈ 9.3 × 10 155 ). Esto no es del todo infinito, pero lo hará: un título nunca puede tener una puntuación de más de 38 veces su longitud (ZZZZ ...), por lo que siempre que ningún título sea mayor que aproximadamente 2 × 10 130 yottabytes, está garantizado que estos estarán al final.
    • : de lo contrario:
    • (... )÷⍴⍵: divide la puntuación por la longitud de después de calcularla:
      • G←(⊂⎕A),(⎕UCS 96+⍳26): almacenar en G mayúsculas y minúsculas
      • (⎕UCS 32+⍳94)~'`',⎕D,∊G: los caracteres ASCII imprimibles, excepto letras, dígitos, espacios y '`', que son los caracteres para los que se resta un punto. (Esto es más corto que escribirlos todos, porqueG se usa más adelante).
      • +/⍵∊: cuenta la cantidad de estos caracteres en
      • -: restar esto de:
      • +/∊1.5 1×(⍳×∊⍨)∘⍵¨G: la suma de 1.5 × las puntuaciones para las mayúsculas, y 1 × las puntuaciones para las letras minúsculas.
    • -⍨: luego, reste el total de los números en :
      • ⍵⊂⍨⍵∊⎕D: encontrar los grupos de dígitos en
      • '0',: agregar '0', para evitar que la lista esté vacía
      • ⍎¨: evalúa cada cadena
      • +/: Encuentra la suma
marinus
fuente
En lugar de que !99puedas usar⌊/⍬
Adám
1
Me encanta mirar código largo en APL. Me hace sentir que el mundo es mucho más grande que yo. Y me gustan los símbolos.
Conor O'Brien
2

Lua 5.3, 366364 Bytes

r={}for i,s in ipairs(arg)do n=0 s:gsub("%l",function(a)n=n+(a:byte()-96)end):gsub("%u",function(a)n=n+(a:byte()-64)*1.5 end):gsub("%p",function(a)n=n-1 end):gsub("^R?.- R.- ?R?",function()n=math.huge end)m=n/utf8.len(s)s:gsub("%d+",function(a)m=m-a end)table.insert(r,{s=s,n=m})end table.sort(r,function(a,b)return a.n<b.n end)for i,v in ipairs(r)do print(v.s)end

Este código solo funciona en Lua 5.3 porque necesita tratar con caracteres Unicode. Si no le importa Unicode, reemplace "utf8" con "string" y funcionará bien con Lua 5.2 o 5.1.

Toma sus entradas de los argumentos de la línea de comandos, así que ejecútelo desde la línea de comandos o coloque este código sobre mi respuesta:

arg = {"Title 1", "Title 2", "Title 3"}
Trebuchette
fuente
No tengo Lua 5.3 en mi máquina, pero he seguido su sugerencia de intercambio utf8con stringel Ideone y no tengo salida.
AdmBorkBork
@TimmyD ver mi edición
Trebuchette
Bueno. Salsa. Y (arg)está sentado allí mirándome a la cara. Esta pregunta aparentemente me ha frito el cerebro. Tener un +1.
AdmBorkBork
Con MoonScript, esto es 266 bytes: pastebin.com/wr4qVs5h .
kirbyfan64sos
2

Mathematica, 253 216 bytes (214 caracteres)

r=RegularExpression;c=ToCharacterCode;f=SortBy[Tr@Flatten@Reap[StringCases[#,
{r@"(\\bR.*)+"->∞,r@"\\d+":>0Sow@-FromDigits@"$0",r@"[a-z]":>c@"$0"-96,
r@"[A-Z]":>1.5c@"$0"-96,r@"[!-/:-@[-_{-~]"->-1}]/StringLength@#]&]

Llame a la función como f[{"42", "9 Kings", "1:8", "7th"}]; devolverá una lista ordenada de las entradas.

¡Apenas lo logré! La coincidencia de patrones de Mathematica no es tan concisa cuando hay cadenas involucradas, y esos largos nombres me matan. Los dos bytes adicionales son para el Infinitycarácter unicode.

(Avíseme si he caído en conflicto con las lagunas estándar).

Actualizar

Mirando un poco más de cerca la respuesta de edc65, parece que el OP aceptará una función que clasifica una lista de cadenas. Con eso en mente, podemos usar la forma curry deSortBy (que Mathematica llama la "forma del operador"); con un argumento (la función aplicada a los elementos de la lista para determinar su orden) se comporta como una función que toma un argumento y devuelve la forma ordenada de la entrada; es decir, SortBy[list, f]es equivalente a(SortBy[f])[list] .

Sin golf

Function[{titles},
  SortBy[titles, Function[{str}, (* sort by function value *)
    Total[Flatten[Reap[ (* total up all the parts *)
      StringCases[str, {
        RegularExpression["(\\bR.*){2}"] -> Infinity
          (* matches R at the start of a word twice, adds infinity to the total *),
        RegularExpression["\\d+"] :> 0 * Sow[-FromDigits["$0"]]
          (* matches a number, Sows it for Reap to collect, then multiplies by zero
                                                          to not affect the average *),
        RegularExpression["[a-z]"] :> ToCharacterCode["$0"] - 96
          (* matches a lowercase letter and returns its value *),
        RegularExpression["[A-Z]"] :> 1.5 ToCharacterCode["$0"] - 96
          (* matches an uppercase letter and returns 1.5 its value *),
        RegularExpression["[!-/:-@[-_{-~]"] -> -1
          (* matches a 'grandiose' symbol and returns -1 *)
      }] / StringLength[#] (* averages character values *)
    ]]]
  ]]
]
Campeonato 2012
fuente
1
Buena respuesta, y obtienes una cookie de Internet por usar literalmente "infinito" en tus cálculos ;-).
AdmBorkBork
@TimmyD La belleza del procesamiento matemático simbólico =)
2012rcampion
Probablemente te refieres a 214 caracteres, 216 bytes. Bien hecho, traté de competir pero de ninguna manera
edc65
2

JavaScript (ES6), 210218251

Como una función con un argumento de matriz, devuelto ordenado.

f=L=>(S=s=>([...s].map(c=>t-=(a=s.charCodeAt(l++))>32&a<48|a>57&a<65|a>90&a<96|a>122&a<127?1:a>64&a<123?96-(a<96?a*1.5:a):0,l=t=0),s.split(/\D/).map(n=>t-=n,t/=l),t/!s.split(/\bR/)[2]),L.sort((a,b)=>S(a)-S(b)))

//TEST

test1=['War and Peace','Reading Rainbow: The Best Unicorn Ever','Maus','Home for a Bunny']
test2=['Matthew','Mark','Luke','John','Revelations']
test3=['42','9 Kings','1:8','7th']

;O.innerHTML=f(test1)+'\n\n'+f(test2)+'\n\n'+f(test3);

// The comparing function used to sort, more readable

Sort=s=>(
  t = 0, // running total
  l = 0, // to calc the string length avoiding the '.length' property
  [...s].map(c=>{
    a=s.charCodeAt(l++);
    t-=a>32&a<48|a>57&a<65|a>90&a<96|a>122&a<127
      ? 1 // symbols (ASCII char except space, alphanumeric and backtick)
      : a>64&a<123 
        ? 96-(a<96?a*1.5:a) // alphabetic both upcase and lowcase, and backtick
        // lowcase: 96-a, upcase (64-a)*1.5=>96-a*1.5, backtick is 96 and 96-96 == 0
        : 0 // else space, non ASCII, and numeric : 0
  }),
  t = t/l, // average
  s.split(/\D/).map(n=>t-=n), // sub number values
  f = s.split(/\bR/)[2], // split at words starting with R, if less then 2 f is undefined
  t/!f // dividing by not f I can get the infinity I need
)
<pre id=O></pre>

edc65
fuente
Bien hecho. Para referencia a cualquiera que lea esta respuesta, tuve que cambiar O.innerHTMLa this.InnerHTMLla consola de Firefox.
AdmBorkBork
1

C #, 352 349 bytes

Debido a la magia de linq:

class A{static void Main(string[]a){foreach(var x in a.OrderBy(b=>{var s="0";int j=0;return Regex.Split(b,@"[^\w]+").Count(l=>l[0]=='R')==2?(1/0d):b.Aggregate(0d,(d,e)=>{if(e>47&e<58){s+=e;return d;}d+=(e>64&e<91)?(e-64)*1.5:(e>96&e<123)?e-96:e>32&e<127&e!=96?-1:0;j+=int.Parse(s);s="0";return d;})/b.Length-j-int.Parse(s);}))Console.WriteLine(x);}}

¡Podría haber guardado otros 6 bytes si el backtick se incluyera en la lista de puntuación!

class A
{
    static void Main(string[] a)
    {
        foreach (var x in a.OrderBy(b =>
            {
                var s = "0";
                int j = 0;
                return Regex.Split(b, @"[^\w]+").Count(l => l[0] == 'R') == 2
                    ? (1 / 0d)
                        : b.Aggregate(0d, (d, e) =>
                        {
                            if (e > 47 & e < 58) { s += e; return d; }
                            d += (e > 64 & e < 91) ? (e - 64) * 1.5 : (e > 96 & e < 123) ? e - 96 : e > 32 & e < 127 & e != 96 ? -1 : 0;
                            j += int.Parse(s);
                            s = "0";
                            return d;
                        }) / b.Length - j - int.Parse(s);
            }))
            Console.WriteLine(x);
    }

}
Yitz
fuente
1

Ir, 755 bytes

package main
import("os"
"fmt"
"math"
"bufio"
"regexp"
"sort"
"strconv")
type F float64
type T []F
func(t T)Swap(i,j int){t[i],t[j],S[i],S[j]=t[j],t[i],S[j],S[i]}
func(t T)Len()int{return len(t)}
func(t T)Less(i,j int)bool{return t[i]<t[j]}
var S []string
func main(){var t T
for{b:=bufio.NewReader(os.Stdin)
w,_,_:=b.ReadLine()
if len(w)==0{break}
u:=string(w)
var v F
for _,c:=range u{if 96<c&&c<123{v+=F(c)-F(96)}else
if 64<c&&c<91{v+=(F(c)-64)*1.5}else
if (48>c&&c>32)||(c>57&&c<127){v-=1}}
a:=v/F(len(w))
r,_:=regexp.Compile("[0-9]+")
n:=r.FindAllString(string(w),-1)
for _,x:=range n{y,_:=strconv.Atoi(x);a-=F(y)}
if m,_:=regexp.Match("((^| )R.*){2}",w);m{a=F(math.Inf(1))}
S=append(S,u)
t=append(t,a)}
sort.Sort(t)
for _,o:=range S{fmt.Println(o)}}

La versión formateada:

package main

import (
    "bufio"
    "fmt"
    "math"
    "os"
    "regexp"
    "sort"
    "strconv"
)

type F float64
type T []F

func (t T) Swap(i, j int)      { t[i], t[j], S[i], S[j] = t[j], t[i], S[j], S[i] }
func (t T) Len() int           { return len(t) }
func (t T) Less(i, j int) bool { return t[i] < t[j] }

var S []string

func main() {
    var t T
    for {
        b := bufio.NewReader(os.Stdin)
        w, _, _ := b.ReadLine()
        if len(w) == 0 {
            break
        }
        u := string(w)
        var v F
        for _, c := range u {
            if 96 < c && c < 123 {
                v += F(c) - F(96)
            } else if 64 < c && c < 91 {
                v += (F(c) - 64) * 1.5
            } else if (48 > c && c > 32) || (c > 57 && c < 127) {
                v -= 1
            }
        }
        a := v / F(len(w))
        r, _ := regexp.Compile("[0-9]+")
        n := r.FindAllString(string(w), -1)
        for _, x := range n {
            y, _ := strconv.Atoi(x)
            a -= F(y)
        }
        if m, _ := regexp.Match("((^| )R.*){2}", w); m {
            a = F(math.Inf(1))
        }
        S = append(S, u)
        t = append(t, a)
    }
    sort.Sort(t)
    for _, o := range S {
        fmt.Println(o)
    }
}

La implementación de una interfaz de clasificación personalizada lo hizo más largo de lo esperado. El programa lee desde STDIN hasta que se ingresa el final de una línea en blanco.

Fabian Schmengler
fuente
1

PHP, 362 367 Bytes

<?for(;$w=fgets(STDIN);$S[]=$w){for($l=$i=mb_strlen($w);$i--;){$c=array_sum(unpack("C*",mb_substr($w,$i,1)));96<$c&&$c<123 and $v+=$c-96 or 64<$c&&$c<91 and $v+=1.5*$c-96 or 48<$c&&$c>32||$c>57&&$c<127 and $v-=1;}$v/=$l;preg_match_all("/\d+/",$w,$m);$v-=array_sum($m[0]);preg_match("/((^| )R.*){2}/",$w)&&$v=INF;$t[]=$v;}array_multisort($t,$S);echo join("
",$S);

Versión formateada:

<?php
for (; $w = fgets(STDIN); $S[] = $w) {
    for ($l = $i = mb_strlen($w); $i--;) {
        $c = array_sum(unpack("C*", mb_substr($w, $i, 1)));
        96 < $c && $c < 123 and $v += $c - 96
        or 64 < $c && $c < 91 and $v += 1.5 * $c - 96
        or 48 < $c && $c > 32 || $c > 57 && $c < 127 and $v -= 1;
    }
    $v /= $l;
    preg_match_all("/\d+/", $w, $m);
    $v -= array_sum($m[0]);
    preg_match("/((^| )R.*){2}/", $w) && $v = INF;
    $t[] = $v;
}
array_multisort($t, $S);
echo join("
", $S); 

Lineas interesantes:

$c = array_sum(unpack("C*", mb_substr($w, $i, 1)));

Convierte un solo carácter UTF-8 a sus valores de byte y los suma, de modo que obtengamos el valor real para los caracteres ASCII y un valor superior a 127 para los caracteres multibyte.

96 < $c && $c < 123 and $v += $c - 96
or 64 < $c && $c < 91 and $v += 1.5 * $c - 96
or 48 < $c && $c > 32 || $c > 57 && $c < 127 and $v -= 1;

Hace uso de la baja precedencia del operador andy orpara asignar el valor de carácter en una sola declaración sin if.

Fabian Schmengler
fuente
1

Perl 5 , 190 bytes

sub p{$_=pop;chomp;$c=-y/A-Za-z0-9 \\`//c;map$c+=(32&ord$_?1:1.5)*(31&ord),/[a-z]/gi;$c/=length;map$c-=$_,/\d+/g;$c}say(sort{$y=$b=~/\bR.*\bR/;($x=$a=~/\bR.*\bR/)||$y?$x-$y:(p($a)<=>p$b)}<>)

Pruébalo en línea!

Xcali
fuente