Monday Mini-Golf # 2: Memotong teks panjang


25

Monday Mini-Golf: Serangkaian tantangan pendek , diposting (mudah-mudahan!) Setiap hari Senin.

Banyak aplikasi web (terutama media sosial) secara otomatis memotong teks yang panjang sehingga sesuai dengan pemformatan aplikasi. Dalam tantangan ini, kita akan membuat algoritma untuk secara otomatis memangkas satu bagian teks ke panjang tertentu.

Tantangan

Tujuan dari tantangan ini adalah untuk menulis program atau fungsi yang menggunakan dua argumen:

  • T , teks untuk dipotong.
  • L , panjang maksimum untuk kembali.

Dan mengembalikan T , terpotong dengan logika berikut:

  • Jika panjang T kurang dari atau sama dengan L , pemotongan tidak diperlukan. Kembalikan string asli.
  • Potong T untuk panjang L -2. Jika ini tidak mengandung spasi atau tanda hubung, kembalikan T terpotong tepat ke L-3 karakter, diikuti oleh elipsis ....
  • Jika tidak, potong ujung hasil hingga ruang terakhir atau tanda hubung. Tambahkan elipsis ...dan kembalikan hasilnya.

Detail

  • T dan L dapat diambil dalam urutan dan format apa pun.
  • Anda dapat berasumsi bahwa 3 < L <2 31 .
  • Anda tidak boleh menggunakan Ellipsis Horizontal U + 2026 ; Anda harus menggunakan tiga periode.
  • Input tidak akan mulai dengan spasi atau tanda hubung.
  • Input tidak akan berisi spasi kosong selain spasi biasa. (Tidak ada tab, baris baru, dll.)

Kasus uji

Input:

"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

Output:

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

(Perhatikan bahwa kutipan hanya untuk menentukan bahwa ini adalah string; tidak perlu dimasukkan.)

Mencetak gol

Ini adalah , jadi kode terpendek yang valid dalam byte menang. Tiebreaker pergi ke pengiriman yang mencapai jumlah byte terakhirnya terlebih dahulu. Pemenang akan dipilih Senin depan, 5 Oktober. Semoga beruntung!

Sunting: Selamat kepada pemenang Anda, @Jakube dengan Pyth lagi, dengan 25 byte!


7
Jawaban untuk tantangan ini harus merupakan fitur standar dalam bahasa masing-masing. Terlalu sering saya melihat UI yang menampilkan trunca buruk ...
Sanchises

1
... "Kalau tidak, potong ujung hasil hingga dan" TIDAK "termasuk spasi atau tanda hubung terakhir." Kanan?
anatolyg

Apakah teks memiliki tab?
kirbyfan64sos

@anatolyg Tidak, karena dengan itu ruang terakhir atau tanda hubung akan muncul sebelum elipsis.
ETHproductions

@ kirbyfan64sos Tidak. Saya akan menambahkannya ke bagian Detail.
ETHproductions

Jawaban:


12

Pyth, 25 byte

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

Cobalah online: Demonstrasi atau Test Suite

Penjelasan:

+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
Saya suka cara kode tertinggal di akhir ...
mathmandan

7

Perl, 69 59 52 byte

51 kode byte + 1 byte baris perintah. Mengasumsikan input numerik diizinkan untuk diberikan dengan parameter -i.

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

Pemakaian:

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

Format input mengikuti contoh input.


1
Nama yang dikenal dari Anarchy Golf. Selamat datang!
xnor

7

JavaScript (ES6), 123 78 67 61 byte

Saya tidak berharap dapat mengurangi ini begitu banyak, tetapi ternyata sambatan / ganti kombo mampu menutupi setiap kasus di mana pemotongan diperlukan.

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

Argumen pertama adalah string, kedua adalah panjangnya. Terima kasih khusus kepada edc65 untuk optimasi pemeriksaan panjang!

Berikut kode aslinya (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
Pintar! +1. Kiat: seringkali Anda tidak perlu .lengthmemeriksa panjang (T,L)=>T[L]?T.slice(0,L-2).replace(/([ -][^ -]*|.)$/,'...'):Tskor senar 61
edc65

@ edc65 Doh! Saya telah mencari pengoptimalan pada pemeriksaan panjang, berpikir harus ada beberapa cara untuk mengurangi itu, tetapi metode Anda tidak terpikir oleh saya. Saran yang sangat baik! : D
Mwr247

Anda dapat mengganti [ -][^ -]dengan \s\Suntuk menyimpan 5 byte lebih
Shaun H

Solusi hebat! @ShaunH, jika dia melakukan itu tidak akan bekerja untuk tanda hubung, tentu?
Jarmex

@Jarmex Otak konyol, ya itu pasti tidak.
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 tidak memiliki banyak perintah manipulasi string, jadi kita perlu menemukan indeks terakhir secara manual: jika string tidak mengandung string untuk dicari, inString(mengembalikan 0. Kami mencari tanda hubung dan spasi mulai dari setiap posisi mulai 1 untuk L, dan mencatat jumlah terbesar kurang dari atau sama dengan L-3. Jika angka Iitu masih 0, kita gunakan L-3sebagai indeks akhir sebagai gantinya.

Karena keterbatasan kalkulator, indeks string yang dapat dialamatkan terbesar adalah 9999; oleh karena itu, ini akan gagal untuk string yang lebih besar.

Saya mengandalkan perilaku kalkulator untuk secara otomatis menginisialisasi variabel Ike 0, jadi hapus Iatau hapus memori kalkulator Anda sebelum menjalankan.


Ada solusi yang lebih pendek menggunakan daftar untuk menemukan indeks terbesar, tetapi kemudian batas ukurannya adalah ~ 500, bukan 9999.
lirtosiast

4

C # .NET, 187 169 byte

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

ya tentu saja, saya baru saja menghapus spasi untuk mengurangi 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]+'...'

Dipanggil dengan

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

Cukup mudah, mungkin bisa bermain golf lebih lanjut



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

pemakaian:

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

Asli Tidak Disatukan:

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

Ini adalah 386 byte / karakter. Beberapa fitur menarik di sini:

The x[y:z]sintaks sintaksis gula untuk x.measure(y, z), dan mengembalikan subrange dari xmulai ydengan panjang z- untuk string, ini adalah substring. (Ada juga x[y..z]sintaksis, yang merupakan rentang dari indeks y ke z, keduanya inklusif, serta bentang setengah terbuka x[...z]dan x[y...].)

List.lastIndexWheremengambil predikat (yaitu fungsi mengambil elemen daftar dan mengembalikan boolean, yaitu di sini a Callable<Boolean, [Character]>), dan memberikan indeks elemen daftar terakhir tempat predikat terpenuhi (atau nol, jika tidak pernah terpenuhi). Karena string adalah daftar, ini juga berfungsi untuk string.

Hasil dari ini, spaceIndexadalah tipe Integer|Null, atau Integer?untuk pendek - yaitu dapat berupa Integer atau null(satu-satunya nilai tipe Null). (Nama itu spaceIndexberasal dari ketika saya tidak menyadari bahwa -itu juga istimewa - saya kira breakIndexakan lebih baik.)

Dengan exists spaceIndexkita dapat memeriksa apakah spaceIndexitu nol, dan kemudian melakukan sesuatu yang berbeda. (Di dalam ini jika-blok kompiler tahu bahwa itu bukan nol ... tanpa itu akan mengeluh jika saya digunakan spaceIndexuntuk mengakses string.)

Alih-alih fungsi lokal spacePredicatekami juga dapat menggunakan fungsi anonim

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

Ini membawa kita ke 333 karakter:

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

Optimalisasi selanjutnya adalah menggunakan nama variabel dan fungsi yang lebih pendek, yang menurunkan kita sebesar 81 byte menjadi 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]+"...";
}

Fungsi predikat sebenarnya tidak perlu tipe argumennya dideklarasikan, yang dapat disimpulkan oleh kompiler. Sama untuk jenis i(di mana kita masih harus menulis valueuntuk menandainya sebagai deklarasi). Sekarang deklarasi itu cukup pendek untuk muat pada satu baris, membawa kita ke 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]+"...";
}

Alih-alih e == ' ' || e == '-'kita juga dapat menulis e in [' ', '-'](atau e in {' ', '-'}, ini adalah konstruktor yang dapat diubah bukan yang tuple). The inOperator peta dengan metode Category.contains, yang membawa kita ke gagasan bahwa kita dapat melewati bahwa tuple ini containsmetode langsung (itu adalah callable mengambil objek apapun, sehingga juga menerima karakter), tanpa (e) => ...boilerplate (222 bytes):

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

Sebenarnya, kategori lain yang mengandung dua karakter yang sama adalah string dua karakter " -". (Selain itu juga mengandung substringnya, tapi tidak ada salahnya di sini). 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]+"...";
}

Saya kira kita mendapatkan yang terbaik dari baris ini, mari kita beralih ke yang lain ... dua pernyataan pengembalian terakhir memiliki beberapa kesamaan yang dapat kita eksploitasi - mereka hanya berbeda dalam ivs l-3, dan menggunakan itepat ketika itu bukan nol, jika tidak l-3. Untungnya inilah yang elsedibuat untuk 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)] + "...";
}

(Kurung tampaknya diperlukan di sini, karena elsememiliki prioritas lebih rendah daripada [:].) Ini adalah 171 karakter. Sekarang idigunakan hanya sekali, jadi kita bisa inline itu, membawa kita ke 153 karakter:

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

Kami juga dapat mengganti if-return-returnkombinasi ini dengan kombinasi operator thendan elsedalam satu return. ( thenpengembalian adalah operan kedua ketika yang pertama benar, jika tidak nol, yang kemudian memungkinkan elseuntuk mengembalikan operan kedua.`) 131 byte (meskipun beberapa penghematan adalah ruang putih yang akan kita singkirkan):

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

Fungsi yang berisi hanya satu pengembalian dengan ekspresi alternatifnya dapat ditulis dengan notasi "panah gemuk", memberikan 123:

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

Menghapus spasi kosong yang tidak dibutuhkan memberi kita 111 byte terakhir:

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

Sebagai tambahan, berikut adalah fungsi yang mencetak contoh-contoh dari pertanyaan (menggunakan nama tyang digunakan setelah langkah kedua):

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 + sed GNU, 65 byte

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

Ini adalah pekerjaan untuk sed! Tapi saya perlu shell untuk mendapatkan batas panjang (mungkin Perl akan lebih baik). Bagian sed mengembang ke urutan yang cukup sederhana, dengan lompatan bersyarat ketika kita selesai:

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

1

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

Disebut sebagai

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

1

> <>, 74 byte

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

Solusi ini membutuhkan string yang akan dipotong dan Lsudah ada di tumpukan, dalam urutan itu.

Ada 7 byte terbuang yang disebabkan oleh masalah penyelarasan, masih mencoba untuk golf keluar.


1

C # (157):

Berdasarkan jawaban Salah Alami , tetapi lebih pendek. Kelas string berasal dari IEnumerable<char>, jadi alih-alih T.Contains(" ")||T.Contains("-"), saya gunakan " -".Any(x=>T.Contains(x)).

Larutan:

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

Tidak Disatukan:

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

Memperbarui:

Disimpan 6 byte berkat komentar dari SLuck49, menggunakan Any(T.Contains)bukan Any(x=>T.Contains(x)).


1
Karena .Any(x=>T.Contains(x))Anda dapat langsung menggunakan metode ini alih-alih lambda ingin .Any(T.Contains)menyimpan 6 byte
SLuck49

@ Sluck49 terima kasih, perbarui jawaban saya.
Abbas

1

GS2 , 29 byte

Program ini membutuhkan input standar. Baris pertama adalah string, dan yang kedua adalah angka panjang target.

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

Kode GS2 terkadang agak sulit dibaca. :) Inilah beberapa komentar.

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

Menyalin jawaban Kleyguerth pertama, maka nama variabel yang sama ...

Potong string ke bawah dengan 2 karakter, maka sebagian besar pekerjaan dilakukan oleh regex, ganti tanda hubung atau spasi diikuti oleh sejumlah karakter apa pun yang bukan tanda hubung atau spasi di ujung string dengan tanda "." ATAU ganti karakter apa pun di akhir string jika semua karakter sebelumnya bukan tanda hubung atau spasi dengan tanda ".". Lebih sulit untuk dimasukkan ke dalam kata-kata daripada untuk menulis ...

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

Sunting: Sebenarnya, cukup hapus bagian string yang cocok dengan regex dan tambahkan "..." di akhir:

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


1

Bersihkan , 89 byte

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

Cobalah online!

Sebagai fungsi $ :: Int String -> String


0

C # (Visual C # Interactive Compiler) , 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

Didasarkan pada @ Abba's, yang didasarkan pada jawaban @Salah Alami. Alih-alih menggunakan Containsdan Substringpanggilan yang tidak perlu , ia menggunakan IndexOf untuk memeriksa apakah ada tanda hubung atau spasi dalam string terpotong.

Cobalah online!

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.