Incroyable système de tri du bibliothécaire fou


21

C'est la rentrée scolaire! Donc, pour un travail à temps partiel, vous aidez à la bibliothèque de l'école. Le problème est que le bibliothécaire en chef n'a même jamais entendu les mots «Dewey Decimal», et encore moins mis en œuvre ce système. Au lieu de cela, le système de tri utilisé s'est développé "organiquement" à mesure que la bibliothèque s'est agrandie ...

Dans un effort pour garder votre santé mentale, vous avez choisi d'écrire un programme pour vous aider à trier les livres au fur et à mesure de leur retour, car malheur à vous si vous triez les livres mal. (Le bibliothécaire en chef est TRÈS strict.)

Entrée sortie

  • L'entrée sera une liste de titres de livres (hypothétiques), un par ligne, à partir de l'équivalent STDIN / langue.
  • Vous ne pouvez pas accepter plus de 100 livres à la fois (vous ne pouvez en transporter qu'un grand nombre dans la bibliothèque à la fois).
  • Les livres peuvent avoir plusieurs mots dans leurs titres, et ces mots peuvent être séparés par des espaces ou d'autres signes de ponctuation (par exemple, deux points :, un tiret -, etc.).
  • Pour faciliter le calcul, supposez que tous les titres sont UTF-8.

La sortie est les mêmes titres, triés selon les règles ci-dessous, encore une fois par ligne, en équivalent STDOUT / langue.

Les règles de tri

Les livres sont triés numériquement en fonction de leur valeur de caractère moyenne (c.-à-d. La valeur de caractère cumulative divisée par le nombre de caractères dans le titre du livre), comptée selon les règles suivantes:

  • Tous les caractères comptent pour déterminer le nombre de caractères dans un titre.
  • Les lettres minuscules sont comptées par leur position dans l'alphabet. (a = 1, b = 2, ... z = 26)
  • Si le titre contient des majuscules, celles-ci comptent pour 1,5 leur valeur minuscule (A = 1,5, B = 3, ... Z = 39). ("Les majuscules sont importantes!", Explique le bibliothécaire.)
  • Chaque signe / symbole de ponctuation dans cette liste !@#$%^&*()-=_+[]\{}|;':",./<>?~compte -1 à partir de la valeur cumulée avant la moyenne. ("Les titres grandioses ne le sont pas!")
  • Si le titre contient un nombre, écrit en chiffres arabes , ce nombre est soustrait de la valeur moyenne avant le tri. Plusieurs chiffres consécutifs sont traités comme un seul chiffre (par exemple, 42soustraire 42, pas soustraire 4 puis soustraire 2). Les chiffres individuels ne comptent pas pour la valeur cumulative (c'est-à-dire que chaque chiffre contribue à 0), mais comptent pour le nombre de caractères. Notez que cela peut entraîner une valeur négative et doit être traité de manière appropriée. (Selon la rumeur, le bibliothécaire a le béguin pour un professeur de mathématiques depuis plusieurs années, maintenant.)
  • Si le titre contient deux mots distincts qui commencent par un R, le livre obtient une partition «infini» et est jeté dans une pile dans le coin (c'est-à-dire disposé au hasard à la fin de la liste). (Le bibliothécaire a déjà été jeté par une personne portant ces initiales, ou du moins vous l'avez entendu.)
  • Les espaces ne comptent pas pour la valeur cumulée des caractères (c'est-à-dire qu'ils contribuent 0), mais contribuent au nombre de caractères dans un titre.
  • Les caractères qui ne correspondent pas aux règles ci-dessus (par exemple, a ÿ) ne comptent pas pour la valeur de caractère cumulative (c'est-à-dire qu'ils contribuent 0), mais contribuent au nombre de caractères dans un titre.
  • Par exemple, un livre hypothétique ÿÿÿÿÿaurait une «partition» de (0+0+0+0+0) / 5 = 0, mais un livre hypothétique ÿÿyÿÿaurait une «partition» de (0+0+25+0+0) / 5 = 5.
  • Deux livres qui arrivent à "marquer" la même chose peuvent être sortis dans votre choix de commande. (Ils sont sur la même étagère, de toute façon)

Exemple d'entrée 1

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

Exemple de résultat 1 (avec "scores" entre parenthèses pour montrer le raisonnement - vous n'avez pas besoin de les imprimer)

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

Exemple d'entrée 2

Matthew
Mark
Luke
John
Revelations

Exemple de résultat 2 (avec "scores" entre parenthèses pour montrer le raisonnement - vous n'avez pas besoin de les imprimer)

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

Exemple d'entrée 3

42
9 Kings
1:8
7th

Exemple de résultat 3 (avec "scores" entre parenthèses pour montrer le raisonnement - vous n'avez pas besoin de les imprimer)

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

Autres restrictions

  • Il s'agit de Code-Golf, car vous devez garder le programme secret aux yeux du bibliothécaire, et plus le programme est petit, plus il est facile à cacher.
  • Des restrictions standard contre les échappatoires s'appliquent
  • Ne laissez pas le bibliothécaire vous rattraper en vous détendant en passant tout votre temps sur PPCG.

Et si deux livres marquaient exactement la même chose. c'est-à-dire que j'ai lu Rainbow et Ruby Rails
Kishan Kumar

@KishanKumar Dans cet exemple spécifique, "disposés au hasard à la fin de la liste" car ils sont tous les deux à double R. En d'autres termes, faites votre choix. Dans le cas général, si deux mots ont le même score, ils peuvent apparaître dans n'importe quel ordre l'un par rapport à l'autre. J'ajouterai une puce pour clarifier cela.
AdmBorkBork

7
Vous avez besoin d'un mot A pour que votre système ait un nom acronyme. Je recommande le système de tri incroyable de Crazy Librarian: D
Geobits

3
@Geobits Avez-vous une CLASSE?
AdmBorkBork

Les nombres ne sont que des nombres décimaux? Et s'il y en a plusieurs, sont-ils tous soustraits séparément?
Paŭlo Ebermann

Réponses:


5

APL (132)

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

Puisque tout le monde fait la même chose, c'est aussi une fonction qui prend un tableau de titres et le renvoie trié, par exemple:

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

Explication:

  • ⎕ML←3: défini ⎕MLsur 3(pour )
  • ⍵[⍋{... }¨⍵]: trie l'entrée en fonction des valeurs renvoyées par la fonction interne
    • ↑¨⍵⊂⍨⍵≠' ': obtenir le premier caractère de chaque mot
    • 2='R'+.=: voir si deux d'entre eux le sont 'R'.
    • :!99: si oui, retournez 99! (≈ 9,3 × 10 155 ). Ce n'est pas tout à fait l'infini, mais ça va faire: un titre ne peut jamais avoir un score plus de 38 fois sa longueur (ZZZZ ...), donc tant qu'aucun titre n'est plus grand qu'environ 2 × 10 130 yottabytes, il est garanti que ceux-ci seront à la fin.
    • : autrement:
    • (... )÷⍴⍵: divisez le score par la longueur après le calcul:
      • G←(⊂⎕A),(⎕UCS 96+⍳26): stocker dans Gles lettres majuscules et minuscules
      • (⎕UCS 32+⍳94)~'`',⎕D,∊G: les caractères ASCII imprimables, à l'exception des lettres, chiffres, espaces et '`', qui sont les caractères pour lesquels un point est soustrait. (C'est plus court que de les écrire tous, car il Gsera utilisé plus tard.)
      • +/⍵∊: compter le nombre de ces caractères dans
      • -: soustrayez ceci de:
      • +/∊1.5 1×(⍳×∊⍨)∘⍵¨G: la somme de 1,5 × les scores pour les majuscules et 1 × les scores pour les lettres minuscules.
    • -⍨: ensuite, soustrayez le total des nombres dans :
      • ⍵⊂⍨⍵∊⎕D: trouver les groupes de chiffres dans
      • '0',: ajouter '0', pour éviter que la liste soit vide
      • ⍎¨: évaluer chaque chaîne
      • +/: trouver la somme

Au lieu de !99vous pourriez utiliser⌊/⍬
Adám

1
J'adore regarder le code long dans APL. Cela me donne l'impression que le monde est tellement grand que moi. Et j'aime les symboles.
Conor O'Brien

2

Lua 5.3, 366 364 octets

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

Ce code ne fonctionne que dans Lua 5.3 car il doit gérer les caractères Unicode. Si vous ne vous souciez pas d'Unicode, remplacez "utf8" par "string" et cela fonctionnera bien avec Lua 5.2 ou 5.1.

Il prend ses entrées à partir des arguments de ligne de commande, alors exécutez-le à partir de la ligne de commande ou mettez ce code au-dessus de ma réponse:

arg = {"Title 1", "Title 2", "Title 3"}

Je n'ai pas Lua 5.3 sur ma machine, mais j'ai suivi votre suggestion d'échanger utf8avec stringsur Ideone et je n'ai eu aucune sortie.
AdmBorkBork

@TimmyD voir mon montage
Trebuchette

Bien. Sauce. Et (arg)est assis là à me regarder en face. Cette question m'a apparemment frit le cerveau. Avoir un +1.
AdmBorkBork

Avec MoonScript, cela fait 266 octets: pastebin.com/wr4qVs5h .
kirbyfan64sos

2

Mathematica, 253216 octets (214 caractères)

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@#]&]

Appelez la fonction comme f[{"42", "9 Kings", "1:8", "7th"}]; il renverra une liste triée des entrées.

À peine arrivé! La correspondance de motifs de Mathematica n'est pas aussi concise lorsque des chaînes sont impliquées, et je me fais juste tuer par ces noms longs. Les deux octets supplémentaires sont destinés au Infinitycaractère unicode.

(Faites-moi savoir si je suis tombé sous le coup de failles standard.)

Mise à jour

En regardant un peu plus près la réponse d'edc65, il semble que l'OP accepte une fonction qui trie une liste de chaînes. Dans cet esprit, nous pouvons utiliser la forme curry de SortBy(que Mathematica appelle la "forme opérateur"); avec un argument (la fonction appliquée aux éléments de la liste pour déterminer leur ordre), il se comporte comme une fonction qui prend un argument, renvoyant la forme triée de l'entrée; c'est-à-dire SortBy[list, f]est équivalent à (SortBy[f])[list].

Non 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 *)
    ]]]
  ]]
]

1
Bonne réponse, et vous obtenez un cookie Internet pour utiliser littéralement "l'infini" dans vos calculs ;-).
AdmBorkBork

@TimmyD La beauté du traitement mathématique symbolique =)
2012rcampion

Vous voulez probablement dire 214 caractères, 216 octets. Bien joué, j'ai essayé de concourir mais pas du tout
edc65

2

JavaScript (ES6), 210 218 251

En tant que fonction avec un argument de tableau, renvoyé trié.

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>


Bien fait. Pour référence à toute autre personne lisant cette réponse, j'ai dû passer O.innerHTMLà this.InnerHTMLdans la console de Firefox.
AdmBorkBork

1

C #, 352 349 octets

En raison de la magie 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);}}

Aurait pu économiser encore 6 octets si le backtick était inclus dans la liste de ponctuation!

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

}

1

Aller, 755 octets

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 version formatée:

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

L'implémentation d'une interface de tri personnalisée l'a rendue plus longue que prévu. Le programme lit depuis STDIN jusqu'à la fin de la saisie d'une ligne vierge.


1

PHP, 362 367 Octets

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

Version formatée:

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

Lignes intéressantes:

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

Convertit un seul caractère UTF-8 en ses valeurs d'octets et les additionne, de sorte que nous obtenons la valeur réelle pour les caractères ASCII et une valeur supérieure à 127 pour les caractères multi-octets.

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;

Utilise la faible priorité d'opérateur de andet orpour affecter la valeur de caractère dans une seule instruction sans if.


1

Perl 5 , 190 octets

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

Essayez-le en ligne!

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.