Mini-golf n. 2 di lunedì: troncamento del testo lungo


25

Mini-golf del lunedì: una serie di corto sfide da , pubblicate (si spera!) Ogni lunedì.

Molte applicazioni Web (in particolare i social media) troncano automaticamente lunghi passaggi di testo in modo da adattarsi alla formattazione dell'app. In questa sfida, creeremo un algoritmo per tagliare automaticamente un passaggio di testo a una certa lunghezza.

Sfida

L'obiettivo della sfida è scrivere un programma o una funzione che contenga due argomenti:

  • T , il testo da troncare.
  • L , la lunghezza massima da restituire.

E restituisce T , troncato con la seguente logica:

  • Se la lunghezza di T è inferiore o uguale a L , non è necessario alcun troncamento. Restituisce la stringa originale.
  • Tronca T alla lunghezza L -2. Se questo non contiene spazi o trattini, restituisce T troncato esattamente a L -3 caratteri, seguito da un'ellissi... .
  • Altrimenti, tagliare la fine del risultato fino all'ultimo spazio o trattino. Aggiungi un puntino di sospensione ...e restituisci il risultato.

Dettagli

  • T e L possono essere presi in qualsiasi ordine e in qualsiasi formato.
  • Si può presumere che 3 < L <2 31 .
  • Non è possibile utilizzare l'ellissi orizzontale U + 2026 ; devi usare tre periodi.
  • L'input non inizierà con uno spazio o un trattino.
  • L'input non conterrà spazi bianchi diversi dagli spazi regolari. (Nessuna scheda, newline, ecc.)

Casi test

ingressi:

"This is some very long text." 25
"This-is-some-long-hyphen-separated-text." 33
"Programming Puzzles & Code Golf is a question and answer site for programming puzzle enthusiasts and code golfers." 55 
"abcdefghijklmnopqrstuvwxyz" 20
"a b c" 4
"Very long." 100

Uscite:

"This is some very long..."
"This-is-some-long-hyphen..."
"Programming Puzzles & Code Golf is a question and..."
"abcdefghijklmnopq..."
"a..."
"Very long."

(Nota che le virgolette servono solo a specificare che si tratta di stringhe; non è necessario che siano incluse.)

punteggio

Questo è , quindi vince il codice valido più breve in byte. Tiebreaker va all'invio che ha raggiunto per primo il numero di byte finale. Il vincitore verrà scelto lunedì prossimo, 5 ottobre. Buona fortuna!

Modifica: Congratulazioni al tuo vincitore, @Jakube con Pyth di nuovo, con 25 byte!


7
Le risposte a questa sfida dovrebbero essere una caratteristica standard nelle rispettive lingue. Troppo spesso ho visto l'interfaccia utente che presenta una cattiva trunca ...
Sanchises,

1
... "In caso contrario, tagliare la fine del risultato fino a" NOT "e includendo l'ultimo spazio o trattino." Destra?
Anatolyg,

Il testo avrà delle schede?
Kirbyfan64sos,

@anatolyg No, perché allora lo spazio finale o il trattino verrebbero visualizzati prima dei puntini di sospensione.
ETHproductions

@ kirbyfan64sos No. Lo aggiungerò alla sezione Dettagli.
ETHproductions

Risposte:


12

Pyth, 25 byte

+WnzK<zeo}@zN" -"-Q2K"...

Provalo online: Dimostrazione o Test Suite

Spiegazione:

+WnzK<zeo}@zN" -"-Q2K"...  implicit: z = input string, Q = input number
        o        -Q2       order the indices N in [0, 1, ..., Q-3] by
         }@zN" -"            z[T] in " -"
                           (hyphen-indices get sorted to the back)
       e                   take the last such number
     <z                    reduce z to length ^
    K                      save this string to K
+WnzK               K"...  print (K + "...") if z != K else only K

4
Mi piace il modo in cui il codice si interrompe alla fine ...
Mathmandan,

7

Perl, 69 59 52 byte

Codice 51 byte + riga comandi 1 byte. Presuppone che sia consentito immettere un input numerico con il parametro -i.

s/.{$^I}\K.*//&&s/(^([^ -]*).|.*\K[ -].*)..$/$2.../

Uso:

echo "This-is-some-long-hyphen-separated-text." | perl -p -i"33" entry.pl

7

Python 2, 78 73 byte

t,l=input()
u=t[:l-2]
print(t,u[:max(map(u.rfind,' -'))]+'...')[l<len(t)]

Il formato di input segue l'input di esempio.


1
Un nome familiare di Anarchy Golf. Benvenuto!
xnor

7

JavaScript (ES6), 123 78 67 61 byte

Non mi aspettavo di essere in grado di ridurlo così tanto, ma risulta che la combinazione di giunzione / sostituzione è in grado di coprire tutti i casi in cui è necessario il troncamento.

(T,L)=>T[L]?T.slice(0,L-2).replace(/([ -][^ -]*|.)$/,'...'):T

Il primo argomento è la stringa, il secondo è la lunghezza. Un ringraziamento speciale a edc65 per l'ottimizzazione del controllo della lunghezza!

Ecco il codice originale (123 byte):

(T,L)=>(T.length>L?(S=T.slice(0,L)).slice(0,(m=Math.max(S.lastIndexOf` `,S.lastIndexOf`-`))<0?L-3:Math.min(L-3,m))+'...':T)

4
Intelligente! +1. Suggerimento: spesso non è necessario .lengthcontrollare la lunghezza del (T,L)=>T[L]?T.slice(0,L-2).replace(/([ -][^ -]*|.)$/,'...'):Tpunteggio di una stringa 61
edc65,

@ edc65 Doh! Stavo cercando un'ottimizzazione sul controllo della lunghezza, pensando che ci fosse un modo per ridurlo, ma il tuo metodo non mi era venuto in mente. Suggerimento eccellente! : D
Mwr247,

È possibile sostituire [ -][^ -]con \s\Sper salvare altri 5 byte
Shaun H

Ottima soluzione! @ShaunH, se lo fa non funzionerà sicuramente per i trattini?
Jarmex,

@Jarmex Silly brain, sì, sicuramente no.
Shaun H,

5

TI-BASIC, 87 byte

Prompt L,Str1
For(X,1,L
{inString(Str1," ",X),inString(Str1,"-",X
max(I,max(Ans*(Ans≤L-3->I
End
Str1
If L<length(Ans
sub(Ans,1,I+(L-3)not(I))+"...
Ans

TI-BASIC non ha molti comandi di manipolazione delle stringhe, quindi dobbiamo trovare manualmente l'ultimo indice: se la stringa non contiene la stringa da cercare, inString(restituisce 0. Cerchiamo trattini e spazi a partire da ogni posizione da 1 a L, e registrare il maggior numero minore o uguale a L-3. Se quel numero Iè ancora 0, utilizziamo invece L-3come indice finale.

A causa delle limitazioni della calcolatrice, l'indice indirizzabile più grande di una stringa è 9999; pertanto, ciò fallirà per stringhe più grandi.

Mi affido al comportamento della calcolatrice di inizializzare automaticamente la variabile Isu 0, quindi cancella Io cancella la memoria della tua calcolatrice prima di eseguirla.


Esiste una soluzione più breve che utilizza gli elenchi per trovare il massimo indice, ma il limite di dimensione sarebbe ~ 500, non 9999.
lirtosiast

4

C # .NET, 187 169 byte

Hmm ...

string f(string T,int L){if(T.Length<=L)return T;T=T.Substring(0,L-2);return T.Substring(0,T.Contains(" ")||T.Contains("-")?T.LastIndexOfAny(new[]{' ','-'}):L-3)+"...";}

sì, certo, ho appena rimosso gli spazi per diminuire i byte.
Salah Alami,

3

Python 2, 105 byte

def t(s,l):a=s[:l-2];return s[:max(a.rfind(' '),a.rfind('-'))]+'...'if' 'in a or'-'in a else a[:-1]+'...'

Chiamato con

>>> print t("This is some very long text.", 25)
This is some very long...

1

Groovy, 95 byte

a={T,L->F=T.size()<=L?T:T[0..L-3]
m=F=~'(.*[- ])'
F==T?F:m?m[0][0].trim()+'...':F[0..-2]+'...'}

Abbastanza semplice, probabilmente può essere ulteriormente giocato a golf



1

T-SQL, 145 byte

create proc a(@t varchar(max),@l int)as if LEN(@t)<=@l return @t;set @t = LEFT(@t,@l-3) select LEFT(@t,LEN(@t)-CHARINDEX('-',REVERSE(@t)))+'...'

utilizzo:

exec a("This is some very long text.", 25) exec a("This-is-some-long-hyphen-separated-text.", 33)



1

Ceylon 386 333 252 230 222 216 171 153 131 111

String t(String s,Integer l)=>s.size<l then s else s[0:(s[0:l-2].lastIndexWhere(" -".contains)else l-3)]+"...";

Originale Ungolfed:

String truncate(String text, Integer length) {
    if(text.size < length) {
        return text;
    }
    Boolean spacePredicate(Character char) {
        return char == ' ' || char == '-';
    }
    Integer? spaceIndex = text[0:length-2].lastIndexWhere(spacePredicate);
    if(exists spaceIndex) {
        return text[0:spaceIndex] + "...";
    }
    return text[0:length-3]+"...";
}

Questo è 386 byte / caratteri. Alcune caratteristiche interessanti qui:

La x[y:z]sintassi è zucchero sintattico per x.measure(y, z), e restituisce un sottogruppo di xpartenza ycon lunghezza z- per le stringhe, questa è una sottostringa. (Esiste anche una x[y..z]sintassi, che è un intervallo dall'indice y alla z, sia inclusivo, sia span semiaperto x[...z]e x[y...].)

List.lastIndexWhereaccetta un predicato (ovvero una funzione che prende un elemento elenco e restituisce un valore booleano, ovvero qui a Callable<Boolean, [Character]>) e fornisce l'indice dell'ultimo elemento elenco in cui è soddisfatto il predicato (o null, se non viene mai adempiuto). Poiché le stringhe sono elenchi, questo funziona anche per le stringhe.

Il risultato di questo, spaceIndexè di tipo Integer|Null, o Integer?in breve - cioè può essere un numero intero o null(l'unico valore di tipo Null). (Il nome spaceIndexderiva da quando non mi rendevo conto che -era anche speciale - immagino breakIndexsarebbe meglio.)

Con exists spaceIndexpossiamo verificare se spaceIndexnon è nullo e fare qualcosa di diverso allora. (All'interno di questo if-block il compilatore sa che è non null ... senza che si sarebbe lamentato se avessi usato spaceIndexl'accesso alla stringa.)

Invece della funzione locale spacePredicatepossiamo anche usare una funzione anonima

(Character char) => char == ' ' || char == '-'

Questo ci porta a 333 caratteri:

String truncate(String text, Integer length) {
    if(text.size < length) {
        return text;
    }
    Integer? spaceIndex = text[0:length-2].lastIndexWhere(
        (Character char) => char == ' ' || char == '-');
    if(exists spaceIndex) {
        return text[0:spaceIndex] + "...";
    }
    return text[0:length-3]+"...";
}

La prossima ottimizzazione consiste nell'utilizzare nomi di variabili e funzioni più brevi, il che ci porta da 81 byte a 252:

String t(String s, Integer l) {
    if(s.size < l) {
        return s;
    }
    Integer? i = s[0:l-2].lastIndexWhere(
        (Character e) => e == ' ' || e == '-');
    if(exists i) {
        return s[0:i] + "...";
    }
    return s[0:l-3]+"...";
}

La funzione predicato in realtà non ha bisogno del suo tipo di argomento dichiarato, che può essere dedotto dal compilatore. Lo stesso vale per il tipo di i(dove dobbiamo ancora scrivere valueper contrassegnarlo come una dichiarazione). Ora quella dichiarazione è abbastanza breve da stare su una riga, portandoci giù a 230:

String t(String s, Integer l) {
    if(s.size < l) {
        return s;
    }
    value i = s[0:l-2].lastIndexWhere((e) => e == ' ' || e == '-');
    if(exists i) {
        return s[0:i] + "...";
    }
    return s[0:l-3]+"...";
}

Invece di e == ' ' || e == '-'possiamo anche scrivere e in [' ', '-'](o e in {' ', '-'}, questo è un costruttore iterabile anziché uno tuplo). L' inoperatore si associa al metodo Category.contains, che ci porta all'idea che possiamo passare containsdirettamente il metodo di quella tupla (è un callable che prende qualsiasi oggetto, quindi accetta anche il carattere), senza il (e) => ...boilerplate (222 byte):

String t(String s, Integer l) {
    if(s.size < l) {
        return s;
    }
    value i = s[0:l-2].lastIndexWhere([' ', '-'].contains);
    if(exists i) {
        return s[0:i] + "...";
    }
    return s[0:l-3]+"...";
}

In realtà, un'altra categoria contenente gli stessi due caratteri è la stringa di due caratteri " -". (Inoltre contiene anche le sue sottostringhe, ma non fa male qui). 216 byte.

String t(String s, Integer l) {
    if(s.size < l) {
        return s;
    }
    value i = s[0:l-2].lastIndexWhere(" -".contains);
    if(exists i) {
        return s[0:i] + "...";
    }
    return s[0:l-3]+"...";
}

Immagino che abbiamo ottenuto il massimo da questa linea, passiamo agli altri ... le ultime due dichiarazioni di ritorno hanno una somiglianza che possiamo sfruttare - differiscono solo in ivs. l-3e stanno usandoi proprio quando non è nullo, altrimenti l-3. Fortunatamente questo è esattamente ciò per cui l' elseoperatore è fatto!

String t(String s, Integer l) {
    if(s.size < l) {
        return s;
    }
    value i = s[0:l-2].lastIndexWhere(" -".contains);
    return s[0:(i else l-3)] + "...";
}

(Le parentesi sembrano essere necessarie qui, come else ha una precedenza inferiore a [:].) Questo è 171 caratteri. Ora iviene usato solo una volta, quindi possiamo incorporarlo, portandoci a 153 caratteri:

String t(String s, Integer l) {
    if(s.size < l) {
        return s;
    }
    return s[0:(s[0:l-2].lastIndexWhere(" -".contains) else l-3)] + "...";
}

Possiamo anche sostituire questa if-return-returncombinazione con una combinazione di theneelse operatori in uno return. ( thenrestituisce il secondo operando quando il primo è vero, altrimenti nullo, che quindi consente elsedi restituire il secondo operando.) 131 byte (anche se alcuni dei risparmi sono gli spazi bianchi che elimineremo comunque):

String t(String s, Integer l) {
    return s.size < l then s else s[0:(s[0:l-2].lastIndexWhere(" -".contains) else l-3)] + "...";
}

Una funzione che contiene un solo ritorno con un'espressione può in alternativa essere scritta con la notazione "freccia grassa", dando 123:

String t(String s, Integer l) =>
    s.size < l then s else s[0:(s[0:l-2].lastIndexWhere(" -".contains) else l-3)] + "...";

La rimozione dello spazio bianco non necessario ci dà i 111 byte finali:

String t(String s,Integer l)=>s.size<l then s else s[0:(s[0:l-2].lastIndexWhere(" -".contains)else l-3)]+"...";

Come aggiunta, ecco una funzione che stampa gli esempi dalla domanda (usando il nome tche viene usato dopo il secondo passaggio):

shared void testTruncate() {
    value testInputs = {
        ["This is some very long text.", 25],
        ["This-is-some-long-hyphen-separated-text.", 33],
        ["Programming Puzzles & Code Golf is a question and answer site for programming puzzle enthusiasts and code golfers.", 55], 
        ["abcdefghijklmnopqrstuvwxyz", 20],
        ["a b c", 4],
        ["Very long.", 100]
    };
    for(input in testInputs) {
        print(t(*input));
    }
}

1

Shell POSIX + GNU sed, 65 byte

sed -re "s/(.{$1}).+/\1/;T;s/(.*)[- ]...*/\1.../;t;s/...$/.../;:"

Questo è un lavoro fatto per sed! Ma avevo bisogno di shell per ottenere il limite di lunghezza (forse Perl sarebbe meglio). La parte sed si espande in una sequenza abbastanza semplice, con salti condizionati quando finiamo:

s/(.{$1}).+/\1/
T
s/(.*)[- ]...*/\1.../
t
s/...$/.../
:

1

Mathematica 192 byte

t=With[{r=StringTake[#,Min[#2-2,StringLength[#]]],p={"-",Whitespace},e="..."}, 
  Which[StringLength[#]<=#2,#,StringFreeQ[r,p],StringDrop[r,-1]<>e,
   True,StringTake[r,Max[StringPosition[r,p]]-1]<>e]]&

Chiamato come

t["This is some very long text.", 25]

1

> <>, 74 byte

l$-:1)?\~r05.
/?=0:~$<-1
\}:0=  ?\::"- "@=@=+?
>~"..."r\}
/!?     <
>ol?!;

Questa soluzione richiede il troncamento della stringa e L sia già nello stack, in questo ordine.

Ci sono 7 byte sprecati causati da problemi di allineamento, ancora cercando di golf quelli fuori.


1

C # (157):

Basato sulla risposta di Salah Alami , ma più breve. La classe stringa deriva da IEnumerable<char>, quindi invece T.Contains(" ")||T.Contains("-")che uso " -".Any(x=>T.Contains(x)).

Soluzione:

string f(string T,int L){if(T.Length<=L)return T;T=T.Substring(0,L-2);return T.Substring(0," -".Any(T.Contains)?T.LastIndexOfAny(new[]{' ','-'}):L-3)+"...";}

Ungolfed:

string f (string T, int L)
{
    if (T.Length <= L)
        return T;

    T = T.Substring(0, L - 2);

    return T.Substring(0, " -".Any(T.Contains) ? T.LastIndexOfAny(new[]{' ', '-'}) : L - 3) + "...";
}

Aggiornare:

6 byte salvati grazie al commento di SLuck49, usando Any(T.Contains) invece di Any(x=>T.Contains(x)).


1
Perché .Any(x=>T.Contains(x))puoi usare direttamente il metodo invece di un lambda come .Any(T.Contains)salvare 6 byte
SLuck49

@ SLuck49 grazie, aggiornata la mia risposta.
Abbas,

1

GS2 , 29 byte

Questo programma accetta input standard. La prima riga è la stringa e la seconda è il numero della lunghezza target.

2a 0e 56 3c 40 a0 74 20 22 22 04 5d 2e 2a 3f 5b
20 2d 5d 7c 2e 07 2e 2e 2e 9d 20 e4 35

Il codice GS2 può essere un po 'difficile da leggere a volte. :) Ecco alcuni commenti.

2a         # lines - split input on newlines yielding a two element array
0e         # extract-array - pop array, push both elements 
56         # read-num - convert length string to number
3c         # take - truncate the string to specified length
40         # dup - duplicate truncated string on stack
a0         # junk1 - push the last popped value, the un-truncated string
74         # ne - test for inequality
    20     # reverse string
    22 22  # tail tail - remove first two characters

    # regex replace first occurrence of ".*?[ -]|." with "..."
    04 5d 2e 2a 3f 5b 20 2d 5d 7c 2e 07 2e 2e 2e 9d 

    20     # reverse string
e4         # block5 - make a block out of last 5 instructions
35         # when - conditionally execute block

1

Groovy, 56 byte

Ho copiato prima la risposta di Kleyguerth, quindi gli stessi nomi delle variabili ...

Taglia la stringa in basso di 2 caratteri, quindi la maggior parte del lavoro viene eseguita dal regex, sostituisci un trattino o uno spazio seguito da un numero qualsiasi di caratteri che non sono un trattino o uno spazio alla fine della stringa con un "." O sostituire un carattere alla fine della stringa se tutti i caratteri prima non sono né un trattino o uno spazio con un ".". Più difficile da esprimere a parole che scrivere la regex ...

a={T,L->T.size()<=L?T:T[0..L-3].replaceAll("([- ][^ -]*|(?<=[^- ]*).)\$",".")+".."}

Modifica: in realtà, puoi semplicemente rimuovere la parte della stringa che corrisponde al regex e aggiungere "..." alla fine:

a={T,L->T.size()<=L?T:T[0..L-3]-~/[- ][^ -]*$|.$/+"..."}


1

Pulito , 89 byte

import StdEnv
$n l|size l>n=l%(0,last[n:[i\\i<-[2..n]&c<-:l|c==' '||c=='-']]-3)+++"..."=l

Provalo online!

Come una funzione $ :: Int String -> String


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.