Понедельник Мини-гольф № 2: усеченный длинный текст


25

Мини-гольф по понедельникам: серия коротких соревнований по , публикуемых (надеюсь!) Каждый понедельник.

Многие веб-приложения (особенно социальные сети) автоматически обрезают длинные отрывки текста, чтобы они соответствовали форматированию приложения. В этой задаче мы собираемся создать алгоритм для автоматической обрезки фрагмента текста до определенной длины.

Вызов

Цель задачи - написать программу или функцию, которая принимает два аргумента:

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

И возвращает T , усеченный со следующей логикой:

  • Если длина 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
... «В противном случае обрежьте конец результата до« НЕТ », включая последний пробел или дефис». Правильно?
Анатолий

Будет ли в тексте вкладки?
kirbyfan64sos

@anatolyg Нет, потому что тогда перед многоточием появится последний пробел или дефис.
ETHproductions

@ kirbyfan64sos Нет. Я добавлю это в раздел «Детали».
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(/([ -][^ -]*|.)$/,'...'):Tпартитуры 61
edc65

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

Вы можете заменить [ -][^ -]на, \s\Sчтобы сохранить еще 5 байтов
Shaun H

Отличное решение! @ ShaunH, если он сделает это, это не будет работать для дефисов, конечно?
Jarmex

@Jarmex Глупый мозг, да, определенно нет.
Шон Х

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

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

Это 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]>), и дает индекс последнего элемента списка, где предикат выполнен (или ноль, если он никогда не выполняется). Поскольку строки являются списками, это работает и для строк.

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

С помощью exists spaceIndexмы можем проверить, не spaceIndexявляется ли NULL, и сделать что-то другое. (Внутри этого блока if компилятор знает, что он не равен нулю ... без этого он бы пожаловался, если бы я использовал 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]+"...";
}

Я думаю, что мы получили максимальную отдачу от этой строки, давайте обратимся к другим ... последние два оператора return имеют некоторое сходство, которое мы можем использовать - они просто отличаются по 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. ( thenreturn - второй операнд, если первый - true, в противном случае - null, который затем позволяет 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 shell + GNU sed, 65 байт

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

Это работа для седа! Но мне нужен shell, чтобы получить ограничение длины (возможно, Perl был бы лучше). Часть sed расширяется до довольно простой последовательности с условными переходами, когда мы заканчиваем:

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

1

Mathematica 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):

На основании ответа Салаха Алами , но короче. Класс string происходит от 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)+"...";}

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

Обновить:

Сохранено 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 #) , 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.