Quantos caracteres por personagem?

15

Em http://shakespeare.mit.edu/, você pode encontrar o texto completo de cada uma das peças de Shakespeare em uma página (por exemplo, Hamlet ).

Escreva um script que considere o URL de uma peça de teatro de stdin, como http://shakespeare.mit.edu/hamlet/full.html , e produz o número de caracteres de texto que cada personagem de peça falou com stdout, classificados de acordo com quem falou mais.

Os títulos da peça / cena / ato obviamente não contam como diálogo, nem os nomes dos personagens. Texto em itálico e [texto entre colchetes] não são diálogos reais, não devem ser contados. Espaços e outras pontuações no diálogo devem ser contados.

(O formato das peças parece muito consistente, embora eu não tenha olhado para todas elas. Diga-me se eu esqueci alguma coisa. Seu script não precisa funcionar para os poemas.)

Exemplo

Aqui está uma seção simulada de Much Ado About Nothing para mostrar o que eu espero para a saída:

Mais barulho por nada

Cena 0.

Mensageiro

Eu vou.

BEATRICE

Faz.

LEONATO

Você nunca.

BEATRICE

Não.

Saída esperada:

LEONATO 15
Messenger 7
BEATRICE 6

Pontuação

Isso é código de golfe. O menor programa em bytes vencerá.

Passatempos de Calvin
fonte
8
E se alguém fizesse esse desafio de Shakespeare em Shakespeare? Seria incrível se isso era mesmo possível ...
fuandon
Podemos assumir que temos uma lista dos personagens da peça? Ou devemos inferir os caracteres do texto? O último é muito difícil, pois alguns caracteres (por exemplo, o Messenger) têm uma mistura de letras maiúsculas e minúsculas. Outros têm nomes com apenas letras maiúsculas (por exemplo, LEONATO); e alguns desses são nomes compostos.
DavidC
Sim, você deve inferir os nomes. Eles são formatados de forma muito diferente do diálogo, portanto, dado o html que os diferencia não deve ser muito complicado.
22714 Calvin's Hobbies
1
'Todos' devem ser considerados como um personagem separado?
es1024
1
@ es1024 Sim. Qualquer personagem de jogo com um título único é considerado separado, mesmo que o resultado não faça exatamente sentido.
Hobbies de Calvin

Respostas:

4

PHP (240 caracteres)

Divide o html em seqüências de caracteres (usando como delimitador) e executa algumas expressões regulares para extrair o nome e as palavras faladas. Economiza o comprimento das palavras faladas em array. Golfe:

<?@$p=preg_match_all;foreach(explode('/bl',implode(file(trim(fgets(STDIN)))))as$c)if($p('/=s.*?b>(.*?):?</',$c,$m)){$p('/=\d.*?>(.*?)</',$c,$o);foreach($m[1]as$n)@$q[$n]+=strlen(implode($o[1]));}arsort($q);foreach($q as$n=>$c)echo"$n $c\n";

Ungolfed:

<?php
$html = implode(file(trim(fgets(STDIN))));
$arr = explode('/bl',$html);
foreach($arr as $chunk){
    if(preg_match_all('/=s.*?b>(.*?):?</',$chunk,$matches)){
        $name = $matches[1];
        preg_match_all('/=\d.*?>(.*?)</',$chunk,$matches);
        foreach($name as $n)
            @$names[$n] += strlen(implode($matches[1]));
    }
}
arsort($names);
foreach($names as $name=>$count)
    echo "$name $count\n";

Nota: Isso considera 'Todos' como um caractere separado.

Exemplo:

$php shakespeare.php <<< "http://shakespeare.mit.edu/hamlet/full.html"
HAMLET 60063
KING CLAUDIUS 21461
LORD POLONIUS 13877
HORATIO 10605
LAERTES 7519
OPHELIA 5916
QUEEN GERTRUDE 5554
First Clown 3701
ROSENCRANTZ 3635
Ghost 3619
MARCELLUS 2350
First Player 1980
OSRIC 1943
Player King 1849
GUILDENSTERN 1747
Player Queen 1220
BERNARDO 1153
Gentleman 978
PRINCE FORTINBRAS 971
VOLTIMAND 896
Second Clown 511
First Priest 499
Captain 400
Lord 338
REYNALDO 330
FRANCISCO 287
LUCIANUS 272
First Ambassador 230
First Sailor 187
Messenger 185
Prologue 94
All 94
Danes 75
Servant 49
CORNELIUS 45
es1024
fonte
1
Por favor, mostre alguns exemplos de saída.
25414
@DavidCarraher Um exemplo foi adicionado.
es1024
3

Rebol - 556 527

t: complement charset"<"d: charset"0123456789."m: map[]parse to-string read to-url input[any[(s: 0 a: copy[])some["<A NAME=speech"some d"><b>"copy n some t</b></a>(append a trim/with n":")some newline]<blockquote>newline any["<A NAME="some d">"copy q some t</a><br>newline(while[f: find q"["][q: remove/part f next find f"]"]s: s + length? trim head q)|<p><i>some t</i></p>newline][</blockquote>|</body>](foreach n a[m/:n: either none? m/:n[s][s + m/:n]])| skip]]foreach[x y]sort/reverse/skip/compare to-block m 2 2[print[x y]]

Provavelmente isso poderia ser ainda mais complicado, no entanto, é improvável que você fique abaixo das respostas já fornecidas :(

Ungolfed:

t: complement charset "<"
d: charset "0123456789."
m: map []

parse to-string read to-url input [
    any [
        (s: 0 a: copy [])

        some [
            "<A NAME=speech" some d "><b>" copy n some t </b></a>
            (append a trim/with n ":")
            some newline
        ]

        <blockquote> newline
        any [
            "<A NAME=" some d ">" copy q some t </a><br> newline (
                while [f: find q "["] [
                    q: remove/part f next find f "]"
                ]
                s: s + length? trim head q
            )
            | <p><i> some t </i></p> newline
        ]
        [</blockquote> | </body>]
        (foreach n a [m/:n: either none? m/:n [s] [s + m/:n]])

        | skip
    ]
]

foreach [x y] sort/reverse/skip/compare to-block m 2 2 [print [x y]]

Este programa remove [texto entre colchetes] e também apara os espaços em branco ao redor do diálogo. Sem isso, a saída é idêntica à resposta es1024 .

Exemplo:

$ rebol -q shakespeare.reb <<< "http://shakespeare.mit.edu/hamlet/full.html"
HAMLET 59796
KING CLAUDIUS 21343
LORD POLONIUS 13685
HORATIO 10495
LAERTES 7402
OPHELIA 5856
QUEEN GERTRUDE 5464
First Clown 3687
ROSENCRANTZ 3585
Ghost 3556
MARCELLUS 2259
First Player 1980
OSRIC 1925
Player King 1843
GUILDENSTERN 1719
Player Queen 1211
BERNARDO 1135
Gentleman 978
PRINCE FORTINBRAS 953
VOLTIMAND 896
Second Clown 511
First Priest 499
Captain 400
Lord 338
REYNALDO 312
FRANCISCO 287
LUCIANUS 269
First Ambassador 230
First Sailor 187
Messenger 185
Prologue 89
All 76
Danes 51
Servant 49
CORNELIUS 45
draegtun
fonte
0

Lisp comum - 528

(use-package :plump)(lambda c(u &aux(h (make-hash-table))n r p)(traverse(parse(drakma:http-request u))(lambda(x &aux y)(case p(0(when(and n(not(ppcre:scan"speech"(attribute x"NAME"))))(setf r t y(#1=ppcre:regex-replace-all"aside: "(#1#"^(\\[[^]]*\\] |\\s*)"(text x)"")""))(dolist(w n)(incf(gethash w h 0)(length y)))))(1(if r(setf n()r()))(push(intern(text(aref(children x)0)))n)))):test(lambda(x)(and(element-p x)(setf p(position(tag-name x)'("A""b"):test #'string=)))))(format t"~{~a ~a~^~%~}"(alexandria:hash-table-plist h)))

Explicação

Esta é uma versão ligeiramente modificada que adiciona informações de impressão (consulte Colar).

(defun c (u &aux
                 (h (make-hash-table)) ;; hash-table
                 n ;; last seen character name
                 r p
                 )
      (traverse                 ;; traverse the DOM generated by ...
       (parse                   ;; ... parsing the text string
        (drakma:http-request u) ;; ... resulting from http-request to link U
        )

       ;; call the function held in variable f for each traversed element
       (lambda (x &aux y)
         (case p
           (0 ;a
            (when(and n(not(alexandria:starts-with-subseq"speech"(attribute x "NAME"))))
              (setf r t)
              (setf y(#1=ppcre:regex-replace-all"aside: "(#1#"^(\\[[^]]*\\] |\\s*)"(text x)"")""))
              (format t "~A ~S~%" n y) ;; debugging
              (dolist(w n)
                (incf
                    (gethash w h 0) ;; get values in hash, with default value 0
                    (length y)))) ;; length of text
            )
           (1 ;b
            (if r(setf n()r()))
            (push (intern (text (aref (children x)0)))n))))

       ;; but only for elements that satisfy the test predicate
       :test
       (lambda(x)
         (and (element-p x) ;; must be an element node
              (setf p(position(tag-name x)'("A""b"):test #'string=)) ;; either <a> or <b>; save result of "position" in p
              )))

        ;; finally, iterate over the elements of the hash table, as a
        ;; plist, i.e. a list of alternating key values (k1 v1 k2 v2 ...),
        ;; and print them as requested. ~{ ~} is an iteration control format.
  (format t "~&~%~%TOTAL:~%~%~{~a ~a~^~%~}" (alexandria:hash-table-plist h)))

Notas

  • Eu removo o texto entre colchetes, bem como a ocorrência "aparte:" que não está presente entre colchetes (também apare caracteres de espaço em branco). Aqui está um rastro de execução com o texto sendo correspondido e o total para cada caractere, para Hamlet .

  • Como outras respostas, tudo é considerado um personagem. Pode ser tentador adicionar o valor de todos a todos os outros personagens, mas isso seria incorreto, pois "Todos" refere-se aos personagens realmente presentes no palco, o que requer manter um contexto de quem está presente (rastreando a saída "exit") indicações "e" enter "). Isso não foi feito.

coredump
fonte