星期一迷你高尔夫2:截断长文字


25

星期一迷你高尔夫:每周一发布(希望!)一系列简短的挑战赛。

许多网络应用程序(尤其是社交媒体)会自动截断较长的文本,使其适合应用程序的格式。在这个挑战中,我们将创建一种算法来自动将一段文本修剪到一定长度。

挑战

挑战的目标是编写一个带有两个参数的程序或函数:

  • T,要截断的文本。
  • L,返回的最大长度。

并返回T,被以下逻辑截断:

  • 如果T的长度小于或等于L,则不需要截断。返回原始字符串。
  • T截短为长度L -2。如果不包含空格或连字符,则将T截断为正L -3个字符,后跟省略号...
  • 否则,将结果的末尾修剪到最后一个空格或连字符。添加省略号...并返回结果。

细节

  • TL可以采用任何顺序和任何格式。
  • 您可以假设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提交的提交首先达到其最终字节数。下个星期一,10月5日将选出获胜者。祝您好运!

编辑:再次恭喜您的获胜者,@Jakube与Pyth,共25个字节!


7
应对这一挑战的答案应该是各自语言的标准功能。我经常看到UI包含错误的截断功能...
Sanchises

1
...“否则,将结果的末尾剪裁为“ NOT”(包括最后一个空格或连字符)。对?
anatolyg's

文字有标签吗?
kirbyfan64sos 15/09/29

@anatolyg不,因为最后的空格或连字符会出现在省略号之前。
ETHproductions 2015年

@ kirbyfan64sos不。我将其添加到“详细信息”部分。
ETHproductions 2015年

Answers:


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
我喜欢代码最后的结尾方式
mathmandan

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 h!我一直在寻找对长度检查的优化,以为必须采用某种方法来减少长度,但是我没有想到您的方法。很好的建议!:D
Mwr247

您可以替换[ -][^ -]\s\S来保存5个以上的字节
Shaun H

很好的解决方案!@ShaunH,如果他这样做对连字符肯定不会起作用?
Jarmex 2015年

@Jarmex傻脑,是的,它绝对不是。
肖恩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在运行之前删除或清除计算器的内存。


有使用列表,找到最大的指数更短的解决方案,但随后的大小限制是5〜50,而不是9999
lirtosiast

4

C#.NET,187169字节

嗯...

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]语法,这是一个跨度从索引j到z,包括两个端点,以及半开跨度x[...z]x[y...])。

List.lastIndexWhere接受一个谓词(即,一个函数接受一个list元素并返回一个布尔值,即a Callable<Boolean, [Character]>),并给出满足该谓词的最后一个list元素的索引(如果从未满足,则返回null)。因为字符串是列表,所以它也适用于字符串。

其结果spaceIndex是type Integer|NullInteger?简称-即它可以是Integer或null(type的唯一值Null)。(这个名字spaceIndex来自我不知道那-也很特别的时候–我想breakIndex会更好。)

通过exists spaceIndex我们可以检查是否spaceIndex为非空,然后做一些不同的事情。(在此if块中,编译器知道它为非null……如果没有,如果我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]+"...";
}

实际上,另一个包含相同两个字符的类别是两个字符的string " -"。(此外,它还包含其子字符串,但这在这里没有影响)。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语句具有一些相似之处,我们可以利用它们–它们只是在ivs中有所不同l-3,并且i仅在不为null时使用,否则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的组合代替此组合。(第一个操作数为true时,return 为第二个操作数,否则为null,然后允许返回其第二个操作数。)131个字节(尽管有些节省是我们总要摆脱的空白):thenelsereturnthenelse

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/...$/.../;:"

这是为sed做的工作!但是我需要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):

根据Salah Alami回答,但要短一些。该字符串由类派生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) + "...";
}

更新:

感谢SLuck49的注释,使用Any(T.Contains)代替节省了6个字节Any(x=>T.Contains(x))


1
因为.Any(x=>T.Contains(x))您可以直接使用该方法而不是lambda .Any(T.Contains)来节省6个字节
SLuck49

@ SLuck49谢谢,更新了我的答案。
阿巴斯2015年

1

GS2个字节

该程序采用标准输入。第一行是字符串,第二行是目标长度数字。

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个字节

首先复制Kleyguerth的答案,因此使用相同的变量名...

将字符串向下修剪2个字符,然后大部分工作由正则表达式完成,将破折号或空格替换为字符串后面的任意数量的非破折号或空格的字符后加“。”。或如果字符串前面的所有字符都不是破折号或带有“。”的空格,则替换字符串末尾的任何字符。比写正则表达式更难用语言表达...

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

编辑:实际上,可以只删除与正则表达式匹配的字符串部分,并在末尾添加“ ...”:

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


1

干净,89字节

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

在线尝试!

作为功​​能 $ :: Int String -> String


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,基于@Salah Alami的答案。它使用IndexOf来检查截断的字符串中是否存在连字符或空格,而不是使用Contains和不必要的Substring调用。

在线尝试!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.