Perluas ekspansi bash brace

20

Untuk sebagian besar alasan historis, bash cukup membingungkan dan sintaksis dan paradigma pemrograman - ini bisa membuatnya canggung dan terkadang membuat frustasi bermain golf. Namun bash memiliki beberapa trik di lengannya yang sering membuatnya kompetitif dengan skrip utama lainnya. bahasa. Salah satunya adalah ekspansi brace .

Ada dua jenis dasar ekspansi brace:

  • Daftar kawat gigi mungkin berisi daftar string arbitrary yang dipisahkan koma (termasuk duplikat dan string kosong). Misalnya {a,b,c,,pp,cg,pp,}akan diperluas ke a b c pp cg pp(perhatikan spasi di sekitar string kosong).
  • Urutan kawat gigi mungkin berisi titik akhir urutan yang dipisahkan oleh ... Secara opsional, orang lain ..dapat mengikuti, diikuti oleh ukuran langkah. Titik akhir urutan dapat berupa bilangan bulat atau karakter. Urutan akan secara otomatis naik atau turun sesuai dengan titik akhir mana yang lebih besar. Sebagai contoh:
    • {0..15} akan diperluas ke 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    • {-10..-5} akan diperluas ke -10 -9 -8 -7 -6 -5
    • {3..-6..2} akan diperluas ke 3 1 -1 -3 -5
    • {a..f} akan diperluas ke a b c d e f
    • {Z..P..3} akan diperluas ke Z W T Q

Di luar ini, kurung kurawal dan daftar mungkin ada dengan kurung kurawal:

  • {a,b,{f..k},p} akan diperluas ke a b f g h i j k p
  • {a,{b,c}} akan diperluas ke a b c

Kawat gigi mengembang dengan string non-spasi putih di kedua sisi. Sebagai contoh:

  • c{a,o,ha,}t akan diperluas ke cat cot chat ct

Ini juga berfungsi untuk beberapa kawat gigi yang disatukan:

  • {ab,fg}{1..3} akan diperluas ke ab1 ab2 ab3 fg1 fg2 fg3

Ini bisa menjadi sangat kompleks. Sebagai contoh:

  • {A..C}{x,{ab,fg}{1..3},y,} akan diperluas ke Ax Aab1 Aab2 Aab3 Afg1 Afg2 Afg3 Ay A Bx Bab1 Bab2 Bab3 Bfg1 Bfg2 Bfg3 By B Cx Cab1 Cab2 Cab3 Cfg1 Cfg2 Cfg3 Cy C

Namun, jika ada ruang kosong di antara ekspansi, maka mereka hanya memperluas sebagai ekspansi terpisah. Sebagai contoh:

  • {a..c} {1..5} akan diperluas ke a b c 1 2 3 4 5

Perhatikan bagaimana pesanan selalu dipertahankan.


Entri untuk tantangan ini akan memperluas ekspansi bash brace seperti dijelaskan di atas. Khususnya:

  • eval by bash(atau cangkang lain yang melakukan ekspansi serupa) tidak diizinkan
  • kurung kurawal akan selalu berupa angka-ke-angka, huruf kecil ke huruf kecil atau huruf besar ke huruf besar tanpa pencampuran. Angka akan berupa bilangan bulat dalam rentang 32-bit yang ditandatangani. Jika diberikan, ukuran langkah opsional akan selalu menjadi bilangan bulat positif. (Perhatikan bahwa bash juga akan berkembang {A..z}, tetapi ini mungkin diabaikan untuk tantangan ini)
  • masing-masing item dalam kawat gigi daftar akan selalu hanya terdiri dari karakter alfanumerik huruf besar dan kecil (termasuk string kosong)
  • daftar kawat gigi dapat berisi sarang sembarang dari ekspansi penjepit lainnya
  • kurung kurawal dapat digabungkan beberapa kali secara acak. Ini akan dibatasi oleh ingatan bahasa Anda, jadi harapannya adalah bahwa Anda secara teoritis dapat melakukan jumlah gabungan acak tetapi jika / ketika Anda kehabisan memori itu tidak akan diperhitungkan melawan Anda.

Contoh dalam teks di atas berfungsi sebagai testcases. Ringkasnya, dengan setiap jalur input sesuai dengan jalur output yang sama, mereka adalah:

Memasukkan

{0..15}
{-10..-5}
{3..-6..2}
{a..f}
{Z..P..3}
{a,b,{f..k},p}
{a,{b,c}}
c{a,o,ha,}t
{ab,fg}{1..3}
{A..C}{x,{ab,fg}{1..3},y,}
{a..c} {1..5}
{a{0..100..10},200}r

Keluaran

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
-10 -9 -8 -7 -6 -5
3 1 -1 -3 -5
a b c d e f
Z W T Q
a b f g h i j k p
a b c
cat cot chat ct
ab1 ab2 ab3 fg1 fg2 fg3
Ax Aab1 Aab2 Aab3 Afg1 Afg2 Afg3 Ay A Bx Bab1 Bab2 Bab3 Bfg1 Bfg2 Bfg3 By B Cx Cab1 Cab2 Cab3 Cfg1 Cfg2 Cfg3 Cy C
a b c 1 2 3 4 5
a0r a10r a20r a30r a40r a50r a60r a70r a80r a90r a100r 200r
Trauma Digital
sumber
3
Saya melihat ke dalam ini dan itu adalah rasa sakit hanya untuk mengurai karena semua kasus tepi :-(
Neil

Jawaban:

3

Ruby, 405 403 401 400 byte

Seorang pria bijak (Jamie Zawinski) pernah berkata, "Beberapa orang, ketika dihadapkan dengan suatu masalah, berpikir 'Saya tahu, saya akan menggunakan ekspresi reguler.' Sekarang mereka memiliki dua masalah. "

Saya rasa saya tidak menghargai kutipan itu sampai saya mencoba menyelesaikan masalah ini dengan regex rekursif. Awalnya, kasus regex tampak sederhana, sampai saya harus berurusan dengan kasus tepi yang melibatkan surat yang berdekatan dengan tanda kurung, dan kemudian saya tahu bahwa saya berada di neraka.

Bagaimanapun, jalankan online di sini dengan kasus uji

->s{s.gsub!(/{(-?\w+)..(-?\w+)(..(\d+))?}/){x,y=$1,$2;a,b,c=[x,y,$4].map &:to_i
$1[/\d/]?0:(a,b=x,y)
k=a<b ?[*a..b]:[*b..a].reverse
?{+0.step(k.size-1,$4?c:1).map{|i|k[i]}*?,+?}}
r=1
t=->x{x[0].gsub(/^{(.*)}$/){$1}.scan(/(({(\g<1>|,)*}|[^,{}]|(?<=,|^)(?=,|$))+)/).map{|i|i=i[0];i[?{]?r[i]:i}.flatten}
r=->x{i=x.scan(/({(\g<1>)*}|[^{} ]+)/).map(&t)
i.shift.product(*i).map &:join}
s.split.map(&r)*' '}

Tidak Disatukan:

->s{
  s.gsub!(/{(-?\w+)..(-?\w+)(..(\d+))?}/){  # Replace all range-type brackets {a..b..c}
    x,y=$1,$2;a,b,c=[x,y,$4].map &:to_i     # Set up int variables
    $1[/\d/]?0:(a,b=x,y)                    # Use int variables for a,b if they're numbers
    k=a<b ?[*a..b]:[*b..a].reverse          # Create an array for the range in the correct direction
    '{'+                                    # Return the next bit surrounded by brackets
      0.step(k.size-1,$4?c:1).map{|i|k[i]   # If c exists, use it as the step size for the array
      }*','                                 # Join with commas
      +'}'
  }
  r=1                                       # Dummy value to forward-declare the parse function `r`
  t=->x{                                    # Function to parse a bracket block
    x=x[0].gsub(/^{(.*)}$/){$1}             # Remove outer brackets if both are present
                                            # x[0] is required because of quirks in the `scan` function
    x=x.scan(/(({(\g<1>|,)*}|[^,{}]|(?<=,|^)(?=,|$))+)/)
                                            # Regex black magic: collect elements of outer bracket
    x.map{|i|i=i[0];i[?{]?r[i]:i}.flatten   # For each element with brackets, run parse function
  }
  r=->x{                                    # Function to parse bracket expansions a{b,c}{d,e}
    i=x.scan(/({(\g<1>)*}|[^{} ]+)/)        # Regex black magic: scan for adjacent sets of brackets
    i=i.map(&t)                             # Map all elements against the bracket parser function `t`
    i.shift.product(*i).map &:join          # Combine the adjacent sets with cartesian product and join them together
  }
  s.split.map(&r)*' '                       # Split on whitespace, parse each bracket collection
                                            #   and re-join with spaces
}
Nilai Tinta
sumber
2

Python 2.7, 752 728 byte

Wow, ini seperti sekelompok kode golf dalam satu tantangan!

Terima kasih kepada @Neil untuk mempersingkat lambda

def b(s,o,p):
 t,f=s>':'and(ord,chr)or(int,str);s,o=t(s),t(o);d=cmp(o,s)
 return list(map(f,range(s,o+d,int(p)*d)))
def e(s):
 c=1;i=d=0
 while c:d+=-~'{}}'.count(s[i])%3-1;i+=1;c=i<len(s)and 0<d
 return i
def m(s):
 if len(s)<1:return[]
 if','==s[-1]:return m(s[:-1])+['']
 i=0
 while i<len(s)and','!=s[i]:i+=e(s[i:])
 return[s[:i]]+m(s[i+1:])
n=lambda a,b:[c+d for c in a for d in b]or a or b
def p(s):
 h=s.count
 if h('{')<1:return[s]
 f,l=s.index('{'),e(s)
 if h('{')<2and h('..')>0and f<1:s=s[1:-1].split('..');return b(s[0],s[1],s[2])if len(s)>2 else b(s[0],s[1],1)
 if f>0 or l<len(s):return n(p(s[:f]),n(p(s[f:l]),p(s[l:])))
 return sum(map(list,map(p,m(s[1:-1]))),[])
o=lambda s:' '.join(p('{'+s.replace(' ',',')+'}'))

Penjelasan

  • b: menghitung rentang sesuai dengan spesifikasi.
  • e: mengembalikan posisi penjepit dekat terluar pertama. Berulang-ulang.
  • m: membelah elemen terluar di koma. Rekursif.
  • n: menggabungkan array sambil memeriksa kekosongan. Saya tidak bisa mulai and/orbekerja.
  • p: Di mana sebagian besar pekerjaan dilakukan. Periksa semua case (Rentang, daftar saja, perlu digabungkan). Rekursif.
  • o: Apa yang harus diambil input. Memformat input / output ke p.

Saya merasa saya dapat meningkat di beberapa tempat, jadi saya akan mencoba bermain golf lebih banyak. Saya juga harus memberikan penjelasan lebih rinci.

Biru
sumber
Saya akan berharap [c+d for c in a for d in b] or a or buntuk bekerja.
Neil
2

JavaScript (Firefox 30-57), 465 427 425 byte

f=s=>/\{/.test(s)?f(s.replace(/([^,{}]*\{[^{}]*\})+[^,{}]*/,t=>t.split(/[{}]+/).map(u=>u.split`,`).reduce((a,b)=>[for(c of a)for(d of b)c+d]))):s.split`,`.join` `
s=>f(`{${s.split` `}}`.replace(/\{(-?\w+)\.\.(-?\w+)(\.\.(\d+))?\}/g,(m,a,o,_,e)=>{m=(a>'@')+(a>'_');a=parseInt(a,m?36:10);o=parseInt(o,m?36:10);e=+e||1;if(o<a)e=-e;for(r=[];e<0?o<=a:a<=o;a+=e)r.push(m?a.toString(36):a);r=`{${r}}`;return m-1?r:r.toUpperCase()}))

Versi ES6 fberbobot ekstra 10 byte:

f=s=>/\{/.test(s)?f(s.replace(/([^,{}]*\{[^{}]*\})+[^,{}]*/,t=>t.split(/[{}]+/).map(u=>u.split`,`).reduce((a,b)=>[].concat(...a.map(c=>b.map(d=>c+d)))))):s.split`,`.join` `
g=s=>f(`{${s.split` `}}`.replace(/\{(-?\w+)\.\.(-?\w+)(\.\.(\d+))?\}/g,(m,a,o,_,e)=>{m=(a>'@')+(a>'_');a=parseInt(a,m?36:10);o=parseInt(o,m?36:10);e=+e||1;if(o<a)e=-e;for(r=[];e<0?o<=a:a<=o;a+=e)r.push(m?a.toString(36):a);r=`{${r}}`;return m-1?r:r.toUpperCase()}))
h=(s,t=s.replace(/\{[^{}]*\}/,""))=>s!=t?h(t):!/[{}]/.test(s)
<input oninput="o.textContent=h(this.value)?g(this.value):'{Invalid}'"><div id=o>

Penjelasan: Mulai dengan mengubah spasi menjadi koma dan membungkus seluruh string {}untuk konsistensi (terima kasih kepada @Blue untuk idenya). Kemudian mencari semua {..}konstruk dan mengembangkannya menjadi {,}konstruk. Selanjutnya menggunakan rekursi untuk berulang kali memperluas semua {,}konstruksi dari dalam ke luar. Akhirnya, ganti semua koma dengan spasi.

f=s=>/\{/.test(s)?                  while there are still {}s
 f(s.replace(                       recursive replacement
  /([^,{}]*\{[^{}]*\})+[^,{}]*/,    match the deepest group of {}s
  t=>t.match(/[^{}]+/g              split into {} terms and/or barewords
   ).map(u=>u.split`,`              turn each term into an array
   ).reduce((a,b)=>                 loop over all the arrays
    [for(c of a)for(d of b)c+d]))   cartesian product
  ):s.split`,`.join` `              finally replace commas with spaces
s=>f(                               change spaces into commas and wrap
 `{${s.split` `}}`.replace(         match all {..} seqences
   /\{([-\w]+)\.\.([-\w]+)(\.\.(\d+))?\}/g,(m,a,o,_,e)=>{
    m=(a>'@')+(a>'_');              sequence type 0=int 1=A-Z 2=a-z
    a=parseInt(a,m?36:10);          convert start to number
    o=parseInt(o,m?36:10);          convert stop to number
    e=+e||1;                        convert step to number (default 1)
    if(o<a)e=-e;                    check if stepping back
    for(r=[];e<0?o<=a:a<=o;a+=e)    loop over each value
     r.push(m?a.toString(36):a);    convert back to string
    r=`{${r}}`;                     join together and wrap in {}
    return m-1?r:r.toUpperCase()})) convert type 1 back to upper case
Neil
sumber