Mở rộng mở rộng bash brace


20

Vì hầu hết các lý do lịch sử, bash khá khó hiểu về cú pháp và mô hình lập trình - điều này có thể khiến nó trở nên lúng túng và đôi khi làm nản lòng khi chơi golf. ngôn ngữ. Một trong số đó là mở rộng cú đúp .

Có hai loại mở rộng niềng răng cơ bản:

  • Danh sách dấu ngoặc có thể chứa danh sách các chuỗi tùy ý được phân tách bằng dấu phẩy (bao gồm các chuỗi trùng lặp và chuỗi trống). Ví dụ {a,b,c,,pp,cg,pp,}sẽ mở rộng thành a b c pp cg pp(lưu ý các khoảng trắng xung quanh các chuỗi trống).
  • Niềng răng trình tự có thể chứa các điểm cuối trình tự cách nhau bởi ... Tùy chọn khác ..có thể theo sau, theo sau là một kích thước bước. Điểm cuối trình tự có thể là số nguyên hoặc ký tự. Trình tự sẽ tự động tăng hoặc giảm theo điểm cuối nào lớn hơn. Ví dụ:
    • {0..15} sẽ mở rộng sang 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    • {-10..-5} sẽ mở rộng sang -10 -9 -8 -7 -6 -5
    • {3..-6..2} sẽ mở rộng sang 3 1 -1 -3 -5
    • {a..f} sẽ mở rộng sang a b c d e f
    • {Z..P..3} sẽ mở rộng sang Z W T Q

Ngoài ra, niềng răng chuỗi và danh sách có thể tồn tại với niềng răng danh sách:

  • {a,b,{f..k},p} sẽ mở rộng sang a b f g h i j k p
  • {a,{b,c}} sẽ mở rộng sang a b c

Niềng răng mở rộng với các chuỗi không phải khoảng trắng ở hai bên của chúng. Ví dụ:

  • c{a,o,ha,}t sẽ mở rộng sang cat cot chat ct

Điều này cũng hoạt động cho nhiều niềng răng nối với nhau:

  • {ab,fg}{1..3} sẽ mở rộng sang ab1 ab2 ab3 fg1 fg2 fg3

Điều này có thể nhận được khá phức tạp. Ví dụ:

  • {A..C}{x,{ab,fg}{1..3},y,} sẽ mở rộng sang 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

Tuy nhiên, nếu có khoảng trắng giữa các lần mở rộng, thì chúng chỉ đơn giản là mở rộng thành các mở rộng riêng biệt. Ví dụ:

  • {a..c} {1..5} sẽ mở rộng sang a b c 1 2 3 4 5

Lưu ý làm thế nào để trật tự luôn được bảo tồn.


Bài dự thi cho thử thách này sẽ mở rộng bash brace mở rộng như mô tả ở trên. Đặc biệt:

  • eval bởi bash(hoặc các shell khác thực hiện mở rộng tương tự) không được phép
  • niềng răng theo thứ tự sẽ luôn luôn là số từ số đến chữ thường, chữ thường đến chữ thường hoặc chữ hoa thành chữ hoa mà không trộn lẫn. Các số sẽ là số nguyên trong phạm vi được ký 32 bit. Nếu được cung cấp, kích thước bước tùy chọn sẽ luôn là một số nguyên dương. (Lưu ý rằng bash cũng sẽ mở rộng {A..z}, nhưng điều này có thể bị bỏ qua cho thử thách này)
  • các mục riêng lẻ trong dấu ngoặc nhọn sẽ luôn chỉ bao gồm các ký tự chữ và số in hoa và in thường (bao gồm chuỗi trống)
  • danh sách niềng răng có thể chứa các tổ tùy ý của các mở rộng niềng răng khác
  • niềng răng có thể được nối số lượng tùy ý của số lần. Điều này sẽ bị giới hạn bởi bộ nhớ ngôn ngữ của bạn, vì vậy, kỳ vọng là về mặt lý thuyết bạn có thể thực hiện số lượng kết nối tùy ý nhưng nếu / khi bạn hết bộ nhớ sẽ không được tính vào bạn.

Các ví dụ trong văn bản trên phục vụ như là thử nghiệm. Tóm tắt, với mỗi dòng đầu vào tương ứng với cùng một dòng đầu ra, chúng là:

Đầu vào

{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

Đầu ra

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

3
Tôi đã xem xét vấn đề này và chỉ đơn giản là đau đớn khi phân tích vì tất cả các trường hợp cạnh :-(
Neil

Câu trả lời:


3

Ruby, 405 403 401 400 byte

Một người đàn ông khôn ngoan (Jamie Zawinski) đã từng nói: "Một số người, khi gặp vấn đề, hãy nghĩ rằng 'Tôi biết, tôi sẽ sử dụng các biểu thức thông thường.' Bây giờ họ có hai vấn đề."

Tôi không nghĩ rằng tôi hoàn toàn đánh giá cao câu nói đó cho đến khi tôi cố gắng giải quyết vấn đề này bằng regex đệ quy. Ban đầu, các trường hợp regex có vẻ đơn giản, cho đến khi tôi phải xử lý các trường hợp cạnh liên quan đến các chữ cái liền kề với dấu ngoặc, và sau đó tôi biết rằng tôi đang ở trong địa ngục.

Dù sao, chạy nó trực tuyến ở đây với các trường hợp thử nghiệm

->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)*' '}

Ung dung:

->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
}

2

Python 2.7, 752 728 byte

Wow, đây giống như một loạt các mã golf trong một thử thách!

Cảm ơn @Neil vì đã rút ngắn 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(' ',',')+'}'))

Giải trình

  • b: tính toán phạm vi theo thông số kỹ thuật.
  • e: trả về vị trí của nẹp đóng ngoài cùng đầu tiên. Lặp đi lặp lại.
  • m: tách các phần tử ngoài cùng trên dấu phẩy. Đệ quy.
  • n: kết hợp các mảng trong khi kiểm tra trống. Tôi không thể and/orđi làm.
  • p: Trường hợp hầu hết các công việc được thực hiện. Kiểm tra tất cả các trường hợp (Phạm vi, chỉ cần liệt kê, cần kết hợp). Đệ quy.
  • o: Những gì nên có đầu vào. Định dạng đầu vào / đầu ra p.

Tôi cảm thấy mình có thể cải thiện ở một số nơi, vì vậy tôi sẽ cố gắng chơi golf nhiều hơn. Ngoài ra tôi nên đặt chi tiết hơn trong phần giải thích.


Tôi đã mong đợi [c+d for c in a for d in b] or a or bđể làm việc.
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()}))

Phiên bản ES6 fcó trọng lượng thêm 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>

Giải thích: Bắt đầu bằng cách thay đổi khoảng trắng thành dấu phẩy và gói toàn bộ chuỗi {}để thống nhất (cảm ơn @Blue cho ý tưởng). Sau đó tìm kiếm tất cả các {..}cấu trúc và mở rộng chúng thành các {,}cấu trúc. Tiếp theo sử dụng đệ quy để liên tục mở rộng tất cả các {,}cấu trúc từ trong ra ngoài. Cuối cùng thay thế tất cả dấu phẩy bằng dấu cách.

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
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.