Monday Mini-Golf # 2: Obcinanie długiego tekstu

25

Monday Mini-Golf: Seria krótkich wyzwań, opublikowanych (miejmy nadzieję!) W każdy poniedziałek.

Wiele aplikacji internetowych (zwłaszcza mediów społecznościowych) automatycznie obcina długie fragmenty tekstu, aby pasowały do ​​formatowania aplikacji. W tym wyzwaniu stworzymy algorytm do automatycznego przycinania fragmentu tekstu do określonej długości.

Wyzwanie

Celem wyzwania jest napisanie programu lub funkcji, która przyjmuje dwa argumenty:

  • T , tekst do obcięcia.
  • L , maksymalna długość zwrotu.

I zwraca T , obcięty za pomocą następującej logiki:

  • Jeśli długość T jest mniejsza lub równa L , obcięcie nie jest konieczne. Zwraca oryginalny ciąg.
  • Obetnij T do długości L -2. Jeśli nie zawiera spacji ani łączników, zwróć literę T obciętą do dokładnie L -3 znaków, a następnie elipsę ....
  • W przeciwnym razie przytnij koniec wyniku do ostatniej spacji lub łącznika. Dodaj elipsę ...i zwróć wynik.

Detale

  • T i L mogą być przyjmowane w dowolnej kolejności i dowolnym formacie.
  • Możesz założyć, że 3 < L <2 31 .
  • Nie możesz używać poziomej elipsy U + 2026 ; musisz użyć trzech kropek.
  • Dane wejściowe nie będą zaczynać się od spacji ani myślnika.
  • Dane wejściowe nie będą zawierać żadnych białych znaków oprócz zwykłych spacji. (Bez kart, nowych linii itp.)

Przypadki testowe

Wejścia:

"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

Wyjścia:

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

(Uwaga: cudzysłowy mają jedynie na celu określenie, że są to ciągi znaków; nie trzeba ich uwzględniać).

Punktacja

To jest , więc wygrywa najkrótszy prawidłowy kod w bajtach. Tiebreaker przechodzi do przesyłania, które jako pierwsze osiągnęło końcową liczbę bajtów. Zwycięzca zostanie wybrany w poniedziałek, 5 października. Powodzenia!

Edycja: Gratulacje dla zwycięzcy, ponownie @Jakube with Pyth, z 25 bajtami!

ETHprodukcje
źródło
7
Odpowiedzi na to wyzwanie powinny być standardową funkcją w ich językach. Zbyt często widziałem interfejs użytkownika ze złym trunca ...
Sanchises,
1
... „W przeciwnym razie przytnij koniec wyniku do i„ NIE ”, w tym ostatnią spację lub łącznik.” Dobrze?
anatolyg
Czy tekst będzie zawierał jakieś zakładki?
kirbyfan64sos
@anatolyg Nie, ponieważ wtedy przed elipsą pojawi się ostatnia spacja lub łącznik.
ETHprodukcje
@ kirbyfan64sos Nie. Dodam to do sekcji Szczegóły.
ETHprodukcje

Odpowiedzi:

12

Pyth, 25 bajtów

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

Wypróbuj online: pakiet demonstracyjny lub testowy

Wyjaśnienie:

+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
Jakube
źródło
4
Podoba mi się sposób, w jaki kod kończy się na końcu ...
Mathmandan
7

Perl, 69 59 52 bajtów

Kod 51 bajtów + wiersz poleceń 1 bajt. Zakłada, że ​​dane liczbowe można podawać za pomocą parametru -i.

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

Stosowanie:

echo "This-is-some-long-hyphen-separated-text." | perl -p -i"33" entry.pl
Jarmex
źródło
7

Python 2, 78 73 bajtów

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

Format wejściowy jest zgodny z przykładowym wejściem.

xsot
źródło
1
Znajome imię od Anarchy Golf. Witamy!
xnor
7

JavaScript (ES6), 123 78 67 61 bajtów

Nie spodziewałem się, że będę w stanie tak bardzo to ograniczyć, ale okazuje się, że kombinacja łączenia / zamiany jest w stanie pokryć każdy przypadek, w którym potrzebne jest obcięcie.

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

Pierwszy argument to ciąg, drugi to długość. Specjalne podziękowania dla edc65 za optymalizację sprawdzania długości!

Oto oryginalny kod (123 bajty):

(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)
Mwr247
źródło
4
Sprytny! +1. Wskazówka: często nie musisz .lengthsprawdzać długości wyniku struny (T,L)=>T[L]?T.slice(0,L-2).replace(/([ -][^ -]*|.)$/,'...'):T61
edc65
@ edc65 Doh! Szukałem optymalizacji kontroli długości, sądząc, że musi istnieć sposób na jej zmniejszenie, ale twoja metoda nie przyszła mi do głowy. Doskonała sugestia! : D
Mwr247,
Można wymienić [ -][^ -]ze \s\Saby zapisać 5 więcej bajtów
Shaun H
Świetne rozwiązanie! @ShaunH, jeśli to zrobi, to na pewno nie zadziała dla łączników?
Jarmex
@Jarmex Silly brain, tak, zdecydowanie nie.
Shaun H
5

TI-BASIC, 87 bajtów

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 nie ma wielu poleceń manipulacji ciągami, dlatego musimy ręcznie znaleźć ostatni indeks: jeśli ciąg nie zawiera szukanego ciągu, inString(zwraca 0. Szukamy łączników i spacji zaczynając od każdej pozycji od 1 do Li nagrywać największą liczbę mniejszą niż lub równy L-3. Jeśli liczba Ita nadal wynosi 0, L-3zamiast tego używamy indeksu końcowego.

Ze względu na ograniczenia kalkulatora największy adresowalny indeks ciągu wynosi 9999; dlatego nie powiedzie się to w przypadku większych ciągów.

Opieram się na zachowaniu kalkulatora podczas automatycznej inicjalizacji zmiennej Ina 0, więc usuń Ilub wyczyść pamięć kalkulatora przed uruchomieniem.

lirtosiast
źródło
Istnieje krótsze rozwiązanie wykorzystujące listy do znalezienia największego indeksu, ale wtedy limit wielkości wynosiłby ~ 500, a nie 9999.
lirtosiast
4

C # .NET, 187 169 bajtów

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)+"...";}
Salah Alami
źródło
tak, oczywiście, właśnie usunąłem spacje, aby zmniejszyć bajty.
Salah Alami
3

Python 2, 105 bajtów

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

Zadzwoniłem z

>>> print t("This is some very long text.", 25)
This is some very long...
Celeo
źródło
1

Groovy, 95 bajtów

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]+'...'}

Całkiem proste, prawdopodobnie można dalej grać w golfa

Kleyguerth
źródło
1

T-SQL, 145 bajtów

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)))+'...'

stosowanie:

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

Sam cd
źródło
1

Cejlon 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)]+"...";

Niegolfowany oryginał:

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]+"...";
}

To jest 386 bajtów / znaków. Kilka interesujących funkcji tutaj:

x[y:z]Składnia jest cukier syntaktyczny dla x.measure(y, z)i zwraca podzakresu z xzaczynając yo długości z- na smyczki, to podciąg. (Jest równieżx[y..z] składnia, która jest rozpiętością od indeksu y do z, zarówno obejmującymi, jak i półotwartymi zakresami x[...z]i x[y...].)

List.lastIndexWherepobiera predykat (tj. funkcję przyjmującą element listy i zwracającą wartość logiczną, tj. tutaj a Callable<Boolean, [Character]>), i podaje indeks ostatniego elementu listy, w którym predykat jest spełniony (lub null, jeśli nigdy nie jest spełniony). Ponieważ ciągi są listami, działa to również w przypadku ciągów.

Wynikiem tego spaceIndexjest rodzaj Integer|Nulllub Integer?w skrócie - tzn. Może to być liczba całkowita lubnull (jedyna wartość typu Null). (Nazwa spaceIndexpochodzi od tego, kiedy nie zdawałem sobie sprawy, że -to również było specjalne - myślę, że breakIndexbyłoby lepiej.)

Za pomocą exists spaceIndexmożemy sprawdzić, czy spaceIndexwartość nie jest zerowa, i zrobić coś innego. (Wewnątrz tego if-bloku kompilator wie, że nie ma wartości null ... bez tego narzekałby, gdybym użył spaceIndexdostępu do ciągu.)

Zamiast funkcji lokalnej spacePredicate możemy również użyć funkcji anonimowej

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

To prowadzi nas do 333 znaków:

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]+"...";
}

Następną optymalizacją jest użycie krótszych nazw zmiennych i funkcji, co obniża nas o 81 bajtów do 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]+"...";
}

Funkcja predykatu faktycznie nie wymaga zadeklarowania typu argumentu, co może wywnioskować kompilator. To samo dotyczy rodzaju i(gdzie wciąż musimy pisaćvalue aby oznaczyć go jako deklarację). Teraz deklaracja jest wystarczająco krótka, aby zmieścić się w jednej linii, co prowadzi nas do 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]+"...";
}

Zamiast tego e == ' ' || e == '-'możemy również pisać e in [' ', '-'](lub e in {' ', '-'}, to jest konstruktowalny iteracyjny zamiast krotkowego). inOperator odwzorowuje metody Category.contains, która prowadzi nas do tego, że możemy przekazać tę krotka w containssposób bezpośredni (to jest wymagalne zastosowaniem jakiegokolwiek przedmiotu, tak też przyjmując postać), bez (e) => ...boilerplate (222 bajtów):

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]+"...";
}

W rzeczywistości inną kategorią zawierającą te same dwa znaki jest ciąg dwóch znaków " -". (Dodatkowo zawiera także podciągi, ale tutaj nie boli). 216 bajtów.

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]+"...";
}

Chyba mamy najwięcej z tej linii, wróćmy do innych ... dwie ostatnie wypowiedzi powrotne mają pewne podobieństwo, które możemy wykorzystać - po prostu różnią się w iwersetach l-3, a są przy użyciu itylko wtedy, gdy nie jest null, inaczej l-3. Na szczęście właśnie toelse służy operator!

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)] + "...";
}

(Nawiasy wydają się być tutaj potrzebne, ponieważ elsemają niższy priorytet niż [:].) To jest 171 znaków. Teraz ijest używany tylko raz, więc możemy go wstawić, doprowadzając nas do 153 znaków:

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

Możemy również zastąpić tę if-return-returnkombinację kombinacją operatorów theni elsew jednym return. ( thenzwraca drugi argument, gdy pierwszy jest prawdziwy, w przeciwnym razie jest pusty, co pozwalaelse return na zwrócenie drugiego operandu.) 131 bajtów (chociaż niektóre oszczędności to białe przestrzenie, które i tak będziemy się pozbywać):

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

Funkcja zawierająca tylko jeden zwrot z wyrażeniem może być alternatywnie zapisana za pomocą notacji „gruba strzałka”, co daje 123:

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

Usunięcie niepotrzebnych białych znaków daje nam ostatnie 111 bajtów:

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

Jako dodatek, tutaj jest funkcja, która drukuje przykłady z pytania (używając nazwy, tktóra jest używana po kroku drugim):

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));
    }
}
Paŭlo Ebermann
źródło
1

Powłoka POSIX + GNU sed, 65 bajtów

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

To jest praca dla sed! Ale potrzebowałem powłoki, aby uzyskać limit długości (być może Perl byłby lepszy). Część sed rozwija się do dość prostej sekwencji, z warunkowymi skokami po zakończeniu:

s/(.{$1}).+/\1/
T
s/(.*)[- ]...*/\1.../
t
s/...$/.../
:
Toby Speight
źródło
1

Mathematica 192 bajty

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

Nazywany jako

t["This is some very long text.", 25]
Verbeia
źródło
1

> <>, 74 bajty

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

To rozwiązanie wymaga obcięcia łańcucha i L go na stosie w tej kolejności.

Jest 7 zmarnowanych bajtów spowodowanych problemami z wyrównaniem, wciąż próbujących je rozegrać.

Sok
źródło
1

C # (157):

Na podstawie odpowiedzi Salah Alami , ale krótszy. W ciąg klasy wywodzi IEnumerable<char>, więc zamiast T.Contains(" ")||T.Contains("-")używam " -".Any(x=>T.Contains(x)).

Rozwiązanie:

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)+"...";}

Nie golfowany:

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) + "...";
}

Aktualizacja:

Zaoszczędziłem 6 bajtów dzięki komentarzowi SLuck49, używając Any(T.Contains)zamiast Any(x=>T.Contains(x)).

Abbas
źródło
1
Ponieważ .Any(x=>T.Contains(x))możesz bezpośrednio użyć metody zamiast lambda, jak .Any(T.Contains)zapisać 6 bajtów
SLuck49
@ SLuck49 dzięki, zaktualizowałem moją odpowiedź.
Abbas
1

GS2 , 29 bajtów

Ten program pobiera standardowe dane wejściowe. Pierwszy wiersz to ciąg, a drugi to liczba długości docelowej.

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

Czasami kod GS2 może być trudny do odczytania. :) Oto komentarz.

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
rekurencyjny
źródło
1

Groovy, 56 bajtów

Najpierw skopiowałem odpowiedź Kleyguertha, stąd te same nazwy zmiennych ...

Przytnij ciąg znaków w dół o 2 znaki, następnie większość pracy jest wykonywana przez wyrażenie regularne, zamień myślnik lub spację, a następnie dowolną liczbę znaków, które nie są myślnikiem ani spacją na końcu łańcucha na „.” LUB zamień dowolny znak na końcu łańcucha, jeśli wszystkie znaki przed nim nie są kreską ani spacją z „.”. Trudniej jest wyrazić słowami niż napisać wyrażenie regularne ...

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

Edycja: W rzeczywistości można po prostu usunąć część ciągu pasującą do wyrażenia regularnego i dodać „...” na końcu:

a={T,L->T.size()<=L?T:T[0..L-3]-~/[- ][^ -]*$|.$/+"..."}
dbramwell
źródło
1

Czysty , 89 bajtów

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

Wypróbuj online!

Jako funkcja $ :: Int String -> String

Obrzydliwe
źródło
0

C # (interaktywny kompilator Visual C #) , 117 bajtów

a=>b=>a.Length>b?a.Substring(0,(" -".Any(x=>a.IndexOf(x,0,b-2)>-1)?a.LastIndexOfAny(new[]{' ','-'},b-2):b-3))+"...":a

Oparty na @ Abba's, który jest oparty na odpowiedzi @Salah Alami. Zamiast używaćContains i niepotrzebnego Substringwywołania używa IndexOf, aby sprawdzić, czy w skrócie znajduje się myślnik lub spacja.

Wypróbuj online!

Wcielenie ignorancji
źródło