O incrível sistema de classificação do bibliotecário louco

21

Está de volta à temporada escolar! Então, para um emprego de meio período, você está ajudando na biblioteca da escola. O problema é que o bibliotecário chefe nunca ouviu as palavras "Dewey Decimal", muito menos implementou esse sistema. Em vez disso, o sistema de classificação em uso cresceu "organicamente" à medida que a biblioteca se expandia ...

Em um esforço para manter sua sanidade, você optou por escrever um programa para ajudá-lo a classificar os livros à medida que eles são devolvidos, porque ai de você se você classificar os livros de maneira errada. (O bibliotecário-chefe é MUITO rigoroso.)

Entrada / Saída

  • A entrada será uma lista de títulos de livros (hipotéticos), um por linha, de STDIN / idioma equivalente.
  • Você pode assumir não mais do que 100 livros introduzidos por vez (você só pode transportar tantos pela biblioteca de uma só vez).
  • Os livros podem ter várias palavras em seus títulos, e essas palavras podem ser separadas por espaços ou outra pontuação (por exemplo, dois pontos :, um hífen -etc.).
  • Para facilitar o cálculo, suponha que todos os títulos sejam UTF-8.

A saída é os mesmos títulos, classificados de acordo com as regras abaixo, novamente um por linha, para STDOUT / idioma equivalente.

As regras de classificação

Os livros são classificados numericamente com base em seu valor médio de caracteres (ou seja, o valor acumulado de caracteres dividido pelo número de caracteres no título do livro), contados pelas seguintes regras:

  • Todos os caracteres contam para determinar o número de caracteres em um título.
  • Letras minúsculas são contadas por sua posição no alfabeto. (a = 1, b = 2, ... z = 26)
  • Se o título contiver letras maiúsculas, elas contarão com 1,5 valor em minúsculas (A = 1,5, B = 3, ... Z = 39). ("Letras maiúsculas são importantes!", Diz o bibliotecário.)
  • Cada sinal de pontuação / símbolo nesta lista !@#$%^&*()-=_+[]\{}|;':",./<>?~conta -1 do valor acumulado antes da média. ("Os títulos grandiosos não são!")
  • Se o título contiver um número, escrito em algarismos arábicos , esse número será subtraído do valor médio antes da classificação. Vários dígitos consecutivos são tratados como um número (por exemplo, 42subtrai 42, não subtrai 4 e depois subtrai 2). Dígitos individuais não contam para o valor cumulativo (ou seja, cada dígito contribui com 0), mas contam para o número de caracteres. Observe que isso pode resultar em um valor negativo e deve ser tratado adequadamente. (Há rumores de que o bibliotecário tem uma queda por um instrutor de matemática há vários anos.)
  • Se o título contiver duas palavras separadas que começam com um R, o livro recebe uma pontuação de "infinito" e é jogado em uma pilha no canto (ou seja, organizado aleatoriamente no final da lista). (O bibliotecário já foi descartado por uma pessoa com essas iniciais, ou pelo menos você já ouviu falar.)
  • Os espaços não contam para o valor acumulado dos caracteres (ou seja, eles contribuem com 0), mas contribuem para o número de caracteres em um título.
  • Os caracteres que não se encaixam nas regras acima (por exemplo, a ÿ) não contam para o valor acumulado dos caracteres (ou seja, contribuem com 0), mas contribuem para o número de caracteres em um título.
  • Por exemplo, um livro hipotético ÿÿÿÿÿteria uma "pontuação" de (0+0+0+0+0) / 5 = 0, mas um livro hipotético ÿÿyÿÿteria uma "pontuação" de (0+0+25+0+0) / 5 = 5.
  • Dois livros que "pontuam" o mesmo podem ser Saída em sua escolha de ordem. (Eles estão na mesma prateleira, de qualquer maneira)

Exemplo de entrada 1

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

Exemplo de saída 1 (com "pontuações" entre parênteses para mostrar o raciocínio - você não precisa imprimi-las)

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

Exemplo de entrada 2

Matthew
Mark
Luke
John
Revelations

Exemplo de saída 2 (com "pontuações" entre parênteses para mostrar o raciocínio - você não precisa imprimi-las)

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

Exemplo de entrada 3

42
9 Kings
1:8
7th

Exemplo de saída 3 (com "pontuações" entre parênteses para mostrar o raciocínio - você não precisa imprimi-las)

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

Outras restrições

  • Este é o Code-Golf, porque você precisa manter o programa em segredo dos olhos sempre atentos do bibliotecário e, quanto menor o programa, mais fácil é se esconder.
  • Aplicam-se restrições de brecha padrão
  • Não deixe que o bibliotecário o pegue relaxando, gastando todo o seu tempo no PPCG.
AdmBorkBork
fonte
E se dois livros tiverem exatamente a mesma pontuação. ou seja, eu tenho Reading Rainbow e Ruby Rails
Kishan Kumar
@KishanKumar Nesse caso específico, "organizado aleatoriamente no final da lista", pois ambos são R duplo. Em outras palavras, faça a sua escolha. No caso geral, se duas palavras tiverem a mesma pontuação, elas poderão aparecer em qualquer ordem em relação uma à outra. Vou adicionar um marcador para esclarecer isso.
AdmBorkBork
7
Você precisa de uma palavra A para que seu sistema tenha um nome de sigla. Eu recomendo o sistema de classificação surpreendente do Crazy Librarian: D
Geobits
3
@Geobits Você tem CLASS?
AdmBorkBork
Números são apenas números decimais? E se houver vários, todos são subtraídos separadamente?
Paŭlo Ebermann 16/09

Respostas:

5

APL (132)

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

Como todo mundo está fazendo a mesma coisa, essa também é uma função que pega uma matriz de títulos e a devolve classificada, por exemplo:

      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│
└──┴───┴───────┴───┴─────────────┴────────────────┴────┴────┴───────────┴────┴───────┴────┴──────────────────────────────────────┘

Explicação:

  • ⎕ML←3: definido ⎕MLcomo 3(para )
  • ⍵[⍋{... }¨⍵]: classifica a entrada pelos valores retornados da função interna
    • ↑¨⍵⊂⍨⍵≠' ': obtém o primeiro caractere de cada palavra
    • 2='R'+.=: veja se dois deles são 'R'.
    • :!99: se sim, retorne 99! (≈ 9,3 × 10 155 ). Isso não é bem infinito, mas serve: um título nunca pode ter uma pontuação superior a 38 vezes (ZZZZ ...); portanto, desde que nenhum título seja maior que 2 × 10 130 yottabytes, é garantido que estes serão no final.
    • : de outra forma:
    • (... )÷⍴⍵: divida a pontuação pelo comprimento de depois de calculá-la:
      • G←(⊂⎕A),(⎕UCS 96+⍳26): armazenar em Gletras maiúsculas e minúsculas
      • (⎕UCS 32+⍳94)~'`',⎕D,∊G: os caracteres ASCII imprimíveis, exceto letras, dígitos, espaços e '`', que são os caracteres para os quais um ponto é subtraído. (Isso é mais curto do que escrevê-los, porque Gé usado mais tarde.)
      • +/⍵∊: conte a quantidade desses caracteres em
      • -: subtraia isso de:
      • +/∊1.5 1×(⍳×∊⍨)∘⍵¨G: a soma de 1,5 × a pontuação das maiúsculas e 1 × a pontuação das letras minúsculas.
    • -⍨: depois, subtraia o total dos números em :
      • ⍵⊂⍨⍵∊⎕D: encontre os grupos de dígitos em
      • '0',: add '0', para impedir que a lista esteja vazia
      • ⍎¨: avalie cada sequência
      • +/: encontre a soma
marinus
fonte
Em vez de !99você poderia usar⌊/⍬
Adám
11
Eu amo olhar código longo em APL. Isso me faz sentir como se o mundo fosse muito maior que eu. E eu gosto de símbolos.
Conor O'Brien
2

Lua 5.3, 366 364 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

Esse código funciona apenas no Lua 5.3 porque ele precisa lidar com caracteres Unicode. Se você não se importa com Unicode, substitua "utf8" por "string" e ele funcionará bem com Lua 5.2 ou 5.1.

Ele recebe suas entradas dos argumentos da linha de comando, portanto, execute-o na linha de comando ou coloque esse código acima da minha resposta:

arg = {"Title 1", "Title 2", "Title 3"}
Trebuchette
fonte
Eu não tenho Lua 5.3 na minha máquina, mas eu segui a sua sugestão para trocar utf8com stringa Ideone e ficou sem saída.
AdmBorkBork 16/09
@TimmyD see my edit #
Trebuchette
Boa. Molho. E (arg)está sentado lá me encarando. Aparentemente, essa pergunta fritou meu cérebro. Tenha um +1.
AdmBorkBork 16/09
Com o MoonScript, são 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@#]&]

Chame a função como f[{"42", "9 Kings", "1:8", "7th"}]; ele retornará uma lista classificada das entradas.

Apenas conseguiu! A correspondência de padrões do Mathematica não é tão concisa quando estão envolvidas seqüências de caracteres, e eu acabo sendo morto por esses nomes longos. Os dois bytes extras são para o Infinitycaractere unicode.

(Deixe-me saber se eu entrei em contato com quaisquer brechas padrão.)

Atualizar

Observando um pouco mais a resposta do edc65, parece que o OP aceitará uma função que classifica uma lista de strings. Com isso em mente, podemos usar a forma ao curry de SortBy(que o Mathematica chama de "forma do operador"); com um argumento (a função aplicada aos elementos da lista para determinar sua ordem), ele se comporta como uma função que recebe um argumento, retornando a forma classificada da entrada; isto é, SortBy[list, f]é equivalente a(SortBy[f])[list] .

Ungolfed

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 *)
    ]]]
  ]]
]
2012rcampion
fonte
11
Boa resposta, e você recebe um cookie da Internet por literalmente usar "infinito" em seus cálculos ;-).
AdmBorkBork
@TimmyD A beleza do processamento matemático simbólico =)
2012rcampion 6/15
Provavelmente você quer dizer 214 caracteres, 216 bytes. Bem feito, tentei competir mas de jeito nenhum
edc65
2

JavaScript (ES6), 210 218 251

Como uma função com um argumento de matriz, retornado classificado.

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
fonte
Bem feito. Para referência a qualquer pessoa que leia esta resposta, tive que mudar O.innerHTMLpara this.InnerHTMLno console do Firefox.
AdmBorkBork
1

C #, 352 349 bytes

Devido à magia do 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);}}

Poderia ter salvo outros 6 bytes se o backtick fosse incluído na lista de pontuação!

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
fonte
1

Go, 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)}}

A versão formatada:

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)
    }
}

A implementação de uma interface de classificação personalizada tornou-a mais longa do que o esperado. O programa lê de STDIN até o final da entrada ou uma linha em branco é inserida.

Fabian Schmengler
fonte
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);

Versão formatada:

<?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); 

Linhas interessantes:

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

Converte um único caractere UTF-8 em seus valores de bytes e os soma, para obtermos o valor real dos caracteres ASCII e um valor superior a 127 para os 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;

Utiliza baixa precedência do operador ande oratribui o valor do caractere em uma única instrução sem if.

Fabian Schmengler
fonte
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)}<>)

Experimente online!

Xcali
fonte