Понеділок міні-гольф №2: обрізання довгого тексту


25

Понеділок Міні-Гольф: Серія коротких викликів з , що публікуються (сподіваємось!) Кожного понеділка.

Багато веб-додатків (особливо соціальних медіа) автоматично обрізають довгі уривки тексту, щоб вони підходили до форматування програми. У цьому виклику ми створимо алгоритм для автоматичного обрізання уривку тексту на певну довжину.

Виклик

Мета завдання - написати програму або функцію, яка має два аргументи:

  • T , текст для урізання.
  • L , максимальна довжина повернення.

І повертає T , усічений за такою логікою:

  • Якщо довжина Т менша або дорівнює L , усічення не потрібно. Повернути початковий рядок.
  • Скоротіть T до довжини L -2. Якщо в ньому немає пробілів чи дефісів, поверніть T, урізаний точно в L -3 символи, з подальшим еліпсісом ....
  • В іншому випадку підріжте кінець результату до останнього місця або дефісу. Додайте еліпсис ...і поверніть результат.

Деталі

  • T і L можуть прийматися в будь-якому порядку і в будь-якому форматі.
  • Можна припустити, що 3 < L <2 31 .
  • Ви не можете використовувати горизонтальний еліпсис U + 2026 ; потрібно використовувати три періоди.
  • Введення не розпочнеться з пробілу чи дефіса.
  • Вхід не міститиме жодного пробілу, крім звичайних пробілів. (Немає вкладок, нових рядків тощо)

Тестові кейси

Вхідні дані:

"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

Виходи:

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

(Зверніть увагу, що лапки лише для того, щоб вказати, що це рядки; їх не потрібно включати.)

Оцінка балів

Це , тому найкоротший дійсний код у байтах виграє. Tiebreaker переходить до подання, яке першим дійшло до остаточного підрахунку байт. Переможець буде обраний наступного понеділка, 5 жовтня. Удачі!

Редагувати: Вітаю переможця @Jakube з Pyth знову з 25 байтами!


7
Відповіді на цей виклик повинні бути стандартною ознакою у відповідних мовах. Занадто часто я бачив користувальницький інтерфейс, у якому зображена погана магістраль ...
Sanchises,

1
... "Інакше обріжте кінець результату до та" НЕ ", включаючи останній пробіл або дефіс." Правильно?
anatolyg

Чи буде в тексті якісь вкладки?
kirbyfan64sos

@anatolyg Ні, тому що тоді перед еліпсисом з'явиться кінцевий простір або дефіс.
ETHproductions

@ kirbyfan64sos Nope. Я додам це до розділу «Подробиці».
ETHproductions

Відповіді:


12

Pyth, 25 байт

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

Спробуйте в Інтернеті: Демонстрація або Тестовий набір

Пояснення:

+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
Мені подобається, як в кінці
відходить

7

Perl, 69 59 52 байти

51 байт код + 1 байт командного рядка. Припускає, що числовий вхід дозволяється задавати за допомогою параметра -i.

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

Використання:

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

7

Python 2, 78 73 байт

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

Формат введення слід за прикладом введення.


1
Знайоме ім’я від Anarchy Golf. Ласкаво просимо!
xnor

7

JavaScript (ES6), 123 78 67 61 байт

Я не очікував, що зможу так скоротити це, але виявляється, комбінований сплайс / заміна здатний охопити кожен випадок, коли потрібно усічення.

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

Перший аргумент - рядок, другий - довжина. Особлива подяка edc65 за оптимізацію перевірки довжини!

Ось оригінальний код (123 байти):

(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
Розумний! +1. Порада: часто вам не потрібно .lengthперевіряти довжину рядкового рядка (T,L)=>T[L]?T.slice(0,L-2).replace(/([ -][^ -]*|.)$/,'...'):T61
edc65

@ edc65 До! Я шукав оптимізацію щодо перевірки довжини, думаючи, що повинен бути якийсь спосіб цього відрізати, але ваш метод мені не прийшов у голову. Відмінна пропозиція! : D
Mwr247

Ви можете замінити [ -][^ -]з \s\Sбільш , щоб зберегти 5 байт
Shaun H

Чудове рішення! @ShaunH, якщо він це зробить, це не буде працювати для дефісів, безумовно?
Jarmex

@Jarmex Дурний мозок, так, це, безумовно, немає.
Shaun H

5

TI-BASIC, 87 байт

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 не так багато команд для маніпулювання рядками, тому нам потрібно знайти останній індекс вручну: якщо рядок не містить рядка для пошуку, inString(повертається 0. Ми шукаємо дефіси та пробіли, починаючи з кожної позиції з 1 до Lта записувати найбільшу кількість, меншу або рівну L-3. Якщо це число Iвсе ще дорівнює 0, L-3замість цього ми використовуємо як індекс закінчення.

Через обмеження калькулятора найбільший адресний індекс рядка - 9999; тому для великих рядків це не вдасться.

Я покладаюся на поведінку калькулятора, що автоматично ініціалізує змінну Iдо 0, тому Iперед запуском видаліть або очистіть пам'ять калькулятора.


Існує коротше рішення, використовуючи списки, щоб знайти найбільший індекс, але тоді обмеження розміру буде ~ 500, а не 9999.
lirtosiast

4

C # .NET, 187 169 байт

Хм ...

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

так, звичайно, я просто видалив пробіли, щоб зменшити байти.
Салах Аламі

3

Python 2, 105 байт

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

Подзвонив с

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

1

Groovy, 95 байт

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

Досить прямо, можливо, можна буде гольфу далі



1

T-SQL, 145 байт

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

використання:

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



1

Цейлон 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)]+"...";

Оригінальний оригінал:

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

Це 386 байт / символів. Тут є кілька цікавих особливостей:

x[y:z]Синтаксис синтаксичний x.measure(y, z), і повертає поддиапазон , xпочинаючи yз довжиною z- для рядків, це підрядок. (Є також x[y..z]синтаксис, який є прольотом від індексу y до z, як включно, так і напіввідкриті проміжки x[...z]та x[y...].)

List.lastIndexWhereприймає предикат (тобто функція, яка бере елемент списку і повертає булевий, тобто тут a Callable<Boolean, [Character]>), і дає індекс останнього елемента списку, де виконується предикат (або null, якщо він ніколи не виконується). Оскільки рядки є списками, це працює і для рядків.

Результат цього spaceIndexмає тип Integer|Nullабо Integer?короткий - тобто він може бути або цілим чи null(єдиним значенням типу Null). (Назва spaceIndexпоходить, коли я не усвідомлював, що -це також було особливим - я думаю, breakIndexбуло б краще.)

З exists spaceIndexми можемо перевірити , якщо spaceIndexне дорівнює нулю, і зробити що - то інше , то. (Всередині цього if-block компілятор знає, що він не є нульовим ... без цього він скаржився б, якби я використовував spaceIndexдоступ до рядка.)

Замість локальної функції spacePredicateми також можемо використовувати анонімну функцію

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

Це приводить нас до 333 символів:

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

Наступна оптимізація полягає у використанні коротших імен змінних та функцій, що знижує нас на 81 байт до 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]+"...";
}

Функція предиката фактично не потребує оголошеного типу аргументу, який може бути зроблений висновком компілятора. Те саме для типу i(де нам ще треба писати, valueщоб позначити це як декларацію). Тепер ця декларація є достатньо короткою, щоб вміститися в одному рядку, і це знизило нас до 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]+"...";
}

Замість цього e == ' ' || e == '-'ми можемо також написати e in [' ', '-'](або e in {' ', '-'}це ітерабельний конструктор замість кортежу). inОператор переводить до методу Category.contains, який приводить нас до думки , що ми можемо передати цей кортеж containsметоди безпосередньо (це викликається приймаючи будь-який об'єкт, так і приймати характер), без (e) => ...шаблонних (222 байт):

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

Власне, ще одна категорія, що містить ті самі два символи, - це двозначний рядок " -". (Крім того, він також містить свої підрядки, але це тут не шкодить). 216 байт.

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

Я думаю, що ми отримали максимум користі з цього рядка, давайте звернемось до інших ... останні два повернення заяви мають деяку схожість, яку ми можемо використати - вони просто відрізняються iпорівняно з l-3і використовуються iлише тоді, коли це недійсне, інакше l-3. На щастя, саме для цього створений elseоператор!

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

(Тут, здається, дужки потрібні, оскільки elseмає нижчий пріоритет, ніж [:].) Це 171 символ. Тепер iвикористовується лише один раз, тому ми можемо вкласти його в рядки, довівши нас до 153 символів:

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

Ми також можемо замінити цю if-return-returnкомбінацію комбінацією операторів thenта elseоператорів в одному return. ( thenповертає другий операнд, коли перший істинний, інакше нульовий, що потім дозволяє elseповернути свій другий операнд. ") 131 байт (хоча частина економії - це білі проміжки, від яких ми позбудемося все одно):

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

Функцію, яка містить лише одне повернення з виразом, можна альтернативно записати з позначенням "жирова стрілка", даючи 123:

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

Видалення непотрібного пробілу дає нам остаточні 111 байт:

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

Як додаток, ось функція, яка друкує приклади із запитання (використовуючи ім'я, tяке використовується після другого кроку):

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

Оболонка POSIX + sed GNU, 65 байт

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

Це робота, зроблена для sed! Але мені знадобилася оболонка, щоб отримати обмеження довжини (можливо, Perl буде краще). Частина sed розширюється до досить простої послідовності, з умовними стрибками, коли ми закінчимо:

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

1

Математика 192 байти

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

Називається як

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

1

> <>, 74 байти

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

Це рішення вимагає обрізати рядок і Lвже бути в стеці в такому порядку.

Там 7 марних байтів, викликаних проблемами вирівнювання, все ще намагаються виграти їх.


1

C # (157):

Виходячи з відповіді Салама Аламі , але коротше. Клас рядків походить від IEnumerable<char>, тому замість цього T.Contains(" ")||T.Contains("-")я використовую " -".Any(x=>T.Contains(x)).

Рішення:

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

Безголівки:

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

Оновлення:

Збережено 6 байт завдяки коментарю SLuck49, використовуючи Any(T.Contains)замість Any(x=>T.Contains(x)).


1
Бо .Any(x=>T.Contains(x))ви можете безпосередньо використовувати метод замість лямбда, як .Any(T.Contains)зберегти 6 байт
SLuck49

@ SLuck49 спасибі, оновив мою відповідь.
Аббас

1

GS2 , 29 байт

Ця програма бере стандартне введення. Перший рядок - це рядок, а другий - номер цільової довжини.

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

GS2-код може бути часом важко читати. :) Ось якийсь коментар.

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 байт

Спершу скопіював відповідь Клейгерта, звідси ті самі назви змінних ...

Обріжте рядок на 2 символи, тоді більшу частину роботи виконує регулярний вимір, замініть тире або пробіл, за яким слід будь-яка кількість символів, які не є тире або пробіл в кінці рядка символом "." АБО замініть будь-який символ у кінці рядка, якщо всі символи перед ним не є тире або пробіл на ".". Складніше скласти слова, ніж написати регулярний вираз ...

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

Редагувати: насправді, ви можете просто видалити частину рядка, яка відповідає регулярному вираженню, і додати "..." наприкінці:

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



0

C # (Visual C # Interactive Compiler) , 117 байт

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

Виходячи з @ Abba's, яка ґрунтується на відповіді @Salah Alami. Замість використання Containsі непотрібнеSubstring виклику він використовує IndexOf для перевірки наявності дефісу чи пробілу в усіченому рядку.

Спробуйте в Інтернеті!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.