Thứ hai Mini-Golf # 2: Cắt ngắn văn bản dài


25

Thứ hai Mini-Golf: Một loạt các thử thách ngắn , được đăng (hy vọng!) Vào mỗi thứ Hai.

Nhiều ứng dụng web (đặc biệt là phương tiện truyền thông xã hội) tự động cắt ngắn các đoạn văn bản dài để chúng phù hợp với định dạng của ứng dụng. Trong thử thách này, chúng tôi sẽ tạo ra một thuật toán để tự động cắt một đoạn văn bản theo một độ dài nhất định.

Thử thách

Mục tiêu của thử thách là viết một chương trình hoặc hàm có hai đối số:

  • T , văn bản để cắt ngắn.
  • L , chiều dài tối đa để trở về.

Và trả về T , cắt ngắn với logic sau:

  • Nếu độ dài của T nhỏ hơn hoặc bằng L , không cần cắt ngắn. Trả về chuỗi gốc.
  • Cắt ngắn T theo chiều dài L -2. Nếu phần này không chứa dấu cách hoặc dấu gạch nối, hãy trả lại T bị cắt cụt thành các ký tự L -3 chính xác , theo sau là dấu chấm lửng ....
  • Nếu không, hãy cắt phần cuối của kết quả cho đến khoảng trắng hoặc dấu gạch nối cuối cùng. Thêm một dấu chấm lửng ...và trả về kết quả.

Chi tiết

  • TL có thể được thực hiện theo thứ tự và bất kỳ định dạng.
  • Bạn có thể giả sử rằng 3 < L <2 31 .
  • Bạn không được sử dụng Ellipsis ngang U + 2026 ; bạn phải sử dụng ba giai đoạn.
  • Đầu vào sẽ không bắt đầu bằng dấu cách hoặc dấu gạch nối.
  • Đầu vào sẽ không chứa bất kỳ khoảng trắng nào ngoài không gian thông thường. (Không có tab, dòng mới, v.v.)

Các trường hợp thử nghiệm

Đầu vào:

"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

Đầu ra:

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

(Lưu ý rằng các trích dẫn chỉ để xác định rằng đây là các chuỗi; chúng không cần được đưa vào.)

Chấm điểm

Đây là , vì vậy mã hợp lệ ngắn nhất tính bằng byte thắng. Tiebreaker đi đến trình mà đạt đến số byte cuối cùng của nó đầu tiên. Người chiến thắng sẽ được chọn vào thứ Hai tới, ngày 5 tháng 10. Chúc may mắn!

Chỉnh sửa: Xin chúc mừng người chiến thắng của bạn, @Jakube với Pyth một lần nữa, với 25 byte!


7
Câu trả lời cho thách thức này phải là một tính năng tiêu chuẩn trong ngôn ngữ tương ứng của họ. Quá lâu tôi đã thấy giao diện người dùng có tính năng trunca xấu ...
Sanchises

1
... "Nếu không, hãy cắt phần cuối của kết quả lên đến và" KHÔNG "bao gồm dấu cách hoặc dấu gạch nối cuối cùng." Đúng?
anatolyg

Văn bản sẽ có bất kỳ tab?
kirbyfan64sos

@anatolyg Không, bởi vì sau đó khoảng trắng hoặc dấu gạch nối cuối cùng sẽ xuất hiện trước dấu chấm lửng.
Sản xuất ETH

@ kirbyfan64sos Không. Tôi sẽ thêm nó vào phần Chi tiết.
Sản xuất ETH

Câu trả lời:


12

Bình thường, 25 byte

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

Dùng thử trực tuyến: Trình diễn hoặc Test Suite

Giải trình:

+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
Tôi thích cách đoạn mã tắt ở cuối ...
mathmandan

7

Perl, 69 59 52 byte

Mã 51 byte + dòng lệnh 1 byte. Giả sử đầu vào số được phép đưa ra với tham số -i.

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

Sử dụng:

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

Định dạng đầu vào sau đầu vào ví dụ.


1
Một cái tên quen thuộc từ Anarchy Golf. Chào mừng bạn
xnor

7

JavaScript (ES6), 123 78 67 61 byte

Tôi không mong đợi có thể cắt giảm điều này quá nhiều, nhưng hóa ra combo nối / thay thế có thể bao quát mọi trường hợp cần cắt ngắn.

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

Đối số đầu tiên là chuỗi, thứ hai là độ dài. Đặc biệt cảm ơn edc65 để tối ưu hóa kiểm tra độ dài!

Đây là mã gốc (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
Tài giỏi! +1. Mẹo: thường thì bạn không cần .lengthkiểm tra độ dài của chuỗi (T,L)=>T[L]?T.slice(0,L-2).replace(/([ -][^ -]*|.)$/,'...'):Tđiểm 61
edc65

@ edc65 Doh! Tôi đã tìm kiếm một sự tối ưu hóa trong kiểm tra độ dài, nghĩ rằng phải có cách nào đó để giảm bớt điều đó, nhưng phương pháp của bạn đã không xảy ra với tôi. Đề nghị tuyệt vời! : D
Mwr247

Bạn có thể thay thế [ -][^ -]bằng \s\Sđể tiết kiệm thêm 5 byte
Shaun H

Giải pháp tuyệt vời! @ShaunH, nếu anh ta làm điều đó sẽ không làm việc cho các dấu gạch ngang, chắc chắn?
Jarmex

@Jarmex Bộ não ngớ ngẩn, vâng chắc chắn là không.
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 không có nhiều lệnh thao tác chuỗi, vì vậy chúng tôi cần tìm chỉ mục cuối cùng theo cách thủ công: nếu chuỗi không chứa chuỗi để tìm kiếm, inString(trả về 0. Chúng tôi tìm kiếm dấu gạch nối và khoảng trắng bắt đầu ở mọi vị trí từ 1 đến Lvà ghi lại số lớn nhất nhỏ hơn hoặc bằng L-3. Nếu số đó Ivẫn là 0, chúng tôi sử dụng L-3làm chỉ số kết thúc thay thế.

Do các giới hạn của máy tính, chỉ mục địa chỉ lớn nhất của chuỗi là 9999; do đó, điều này sẽ thất bại cho các chuỗi lớn hơn.

Tôi dựa vào hành vi của máy tính là tự động khởi tạo biến Ithành 0, vì vậy hãy xóa Ihoặc xóa bộ nhớ của máy tính trước khi chạy.


Có một giải pháp ngắn hơn bằng cách sử dụng danh sách để tìm chỉ số lớn nhất, nhưng sau đó giới hạn kích thước sẽ là ~ 500, chứ không phải 9999.
lirtosiast

4

C # .NET, 187 169 byte

Hừm ...

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

tất nhiên, tôi chỉ loại bỏ khoảng trắng để giảm 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]+'...'

Được gọi với

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

Khá đơn giản, có lẽ có thể được chơi gôn hơn nữa



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

sử dụng:

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

Ungolfed gốc:

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

Đây là 386 byte / ký tự. Một số tính năng thú vị ở đây:

Các x[y:z]cú pháp là cú pháp đường cho x.measure(y, z), và trả về một subrange của xkhởi điểm yvới chiều dài z- cho chuỗi, đây là một chuỗi. (Ngoài ra còn có x[y..z]cú pháp, là một khoảng từ chỉ số y đến z, bao gồm cả, cũng như các nhịp mở nửa x[...z]x[y...].)

List.lastIndexWhere lấy một vị ngữ (tức là một hàm lấy một phần tử danh sách và trả về một boolean, tức là ở đây một Callable<Boolean, [Character]> ) và đưa ra chỉ mục của phần tử danh sách cuối cùng trong đó vị từ được hoàn thành (hoặc null, nếu nó không bao giờ được hoàn thành). Vì chuỗi là danh sách, điều này cũng hoạt động cho chuỗi.

Kết quả của việc này, spaceIndexthuộc loại Integer|Nullhoặc Integer?viết tắt - nghĩa là nó có thể là Số nguyên hoặc null(giá trị duy nhất của loại Null). (Cái tên spaceIndexnày xuất phát từ khi tôi không nhận ra điều đó -cũng đặc biệt - tôi đoán breakIndexsẽ tốt hơn.)

Với exists spaceIndexchúng tôi có thể kiểm tra nếu spaceIndexkhông phải là null, và sau đó làm một cái gì đó khác nhau. (Bên trong khối if-trình biên dịch này biết rằng nó không phải là null ... nếu không có nó sẽ phàn nàn nếu tôi sử dụng spaceIndexđể truy cập chuỗi.)

Thay vì chức năng cục bộ, spacePredicatechúng ta cũng có thể sử dụng một chức năng ẩn danh

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

Điều này đưa chúng ta đến 333 ký tự:

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

Tối ưu hóa tiếp theo là sử dụng các tên hàm và biến ngắn hơn, đưa chúng ta xuống 81 byte xuống còn 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]+"...";
}

Hàm vị ngữ thực sự không cần loại đối số được khai báo, có thể được trình biên dịch suy ra. Tương tự cho loại i(nơi chúng ta vẫn phải viết valueđể đánh dấu nó là một khai báo). Bây giờ tuyên bố đó đủ ngắn để phù hợp với một dòng, đưa chúng tôi xuống 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]+"...";
}

Thay vì e == ' ' || e == '-'chúng ta cũng có thể viết e in [' ', '-'](hoặc e in {' ', '-'}, đây là một hàm tạo có thể lặp lại thay vì một tuple). Các innhà khai thác bản đồ với phương pháp Category.contains, mà đưa chúng ta đến ý kiến cho rằng chúng ta có thể vượt qua của tuple rằng containsphương pháp trực tiếp (nó là một callable dùng bất cứ đối tượng, vì vậy cũng chấp nhận ký tự), mà không (e) => ...soạn sẵn (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]+"...";
}

Trên thực tế, một thể loại khác chứa hai ký tự giống nhau là chuỗi hai ký tự " -". (Ngoài ra, nó cũng chứa các chuỗi con của nó, nhưng điều đó không gây hại ở đây). 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]+"...";
}

Tôi đoán rằng chúng tôi đã tận dụng tối đa dòng này, chúng ta hãy chuyển sang các dòng khác ... hai câu trả về cuối cùng có một số điểm tương tự mà chúng ta có thể khai thác - chúng chỉ khác nhau iso với l-3và không sử dụng ikhi nó không rỗng, nếu không l-3. May mắn thay, đây là chính xác những gì các elsenhà điều hành được thực hiện cho!

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

(Các dấu ngoặc đơn dường như là cần thiết ở đây, vì elsecó độ ưu tiên thấp hơn [:].) Đây là 171 ký tự. Bây giờ iđược sử dụng chỉ một lần, vì vậy chúng tôi có thể nội tuyến nó, đưa chúng tôi tới 153 ký tự:

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

Chúng ta cũng có thể thay thế if-return-returnsự kết hợp này bằng sự kết hợp của các toán tử thenelsetoán tử trong một return. ( thentrả về là toán hạng thứ hai khi giá trị thứ nhất là đúng, nếu không thì null, sau đó cho phép elsetrả về toán hạng thứ hai của nó. ') 131 byte (mặc dù một số khoản tiết kiệm là khoảng trắng mà chúng ta sẽ thoát khỏi mọi cách):

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

Một hàm chỉ chứa một trả về với một biểu thức có thể được viết bằng ký hiệu "mũi tên béo", đưa ra 123:

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

Xóa khoảng trắng không cần thiết sẽ cho chúng ta 111 byte cuối cùng:

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

Ngoài ra, đây là một chức năng in các ví dụ từ câu hỏi (sử dụng tên tđược sử dụng sau bước hai):

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

Đây là một công việc được thực hiện cho sed! Nhưng tôi cần shell để có giới hạn độ dài trong (có lẽ Perl sẽ tốt hơn). Phần sed mở rộng ra một chuỗi khá đơn giản, với các bước nhảy có điều kiện khi chúng ta kết thúc:

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

1

Toán học 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]]&

Gọi là

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

1

> <>, 74 byte

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

Giải pháp này yêu cầu chuỗi phải được cắt bớt và Lđã có trên ngăn xếp, theo thứ tự đó.

Có 7 byte bị lãng phí gây ra bởi các vấn đề căn chỉnh, vẫn đang cố gắng loại bỏ chúng.


1

C # (157):

Dựa trên câu trả lời của Salah Alami , nhưng ngắn hơn. Lớp chuỗi có nguồn gốc từ IEnumerable<char>, vì vậy thay vì T.Contains(" ")||T.Contains("-"), tôi sử dụng " -".Any(x=>T.Contains(x)).

Dung dịch:

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

Ung dung:

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

Cập nhật:

Đã lưu 6 byte nhờ nhận xét của SLuck49, sử dụng Any(T.Contains)thay vì Any(x=>T.Contains(x)).


1
Để .Any(x=>T.Contains(x))bạn có thể trực tiếp sử dụng phương thức thay vì lambda muốn .Any(T.Contains)lưu 6 byte
SLuck49

@ SLuck49 cảm ơn, cập nhật câu trả lời của tôi.
Abbas

1

GS2 , 29 byte

Chương trình này có đầu vào tiêu chuẩn. Dòng đầu tiên là chuỗi và thứ hai là số độ dài mục tiêu.

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

Mã GS2 đôi khi có thể hơi khó đọc. :) Đây là một số bình luận.

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

Sao chép câu trả lời của Kleyguerth trước, do đó có cùng tên biến ...

Cắt chuỗi xuống 2 ký tự, sau đó hầu hết các công việc được thực hiện bằng biểu thức chính quy, thay thế dấu gạch ngang hoặc khoảng trắng theo sau bởi bất kỳ số ký tự nào không phải là dấu gạch ngang hoặc khoảng trắng ở cuối chuỗi bằng dấu "." HOẶC thay thế bất kỳ ký tự nào ở cuối chuỗi nếu tất cả các ký tự trước đó không phải là dấu gạch ngang hoặc dấu cách bằng dấu ".". Khó nói thành lời hơn viết regex ...

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

Chỉnh sửa: Trên thực tế, chỉ có thể xóa phần của chuỗi khớp với biểu thức chính quy và thêm "..." vào cuối:

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



0

C # (Trình biên dịch tương tác Visual C #) , 117 byte

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

Dựa trên câu trả lời của @ Abba, dựa trên câu trả lời của @Salah Alami. Thay vì sử dụng Containsvà không cần thiếtSubstring cuộc gọi , nó sử dụng IndexOf để kiểm tra xem một dấu gạch nối hoặc dấu cách có tồn tại trong chuỗi bị cắt hay không.

Hãy thử trực tuyến!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.