ขยายการขยาย bash brace


20

ด้วยเหตุผลทางประวัติศาสตร์ส่วนใหญ่การทุบตีนั้นค่อนข้างจะเป็นการหลบเลี่ยงวากยสัมพันธ์และกระบวนทัศน์การเขียนโปรแกรม - สิ่งนี้สามารถทำให้มันอึดอัดใจและบางครั้งก็น่าหงุดหงิดกับการเล่นกอล์ฟอย่างไรก็ตามมันมีเทคนิคเล็กน้อย ภาษา หนึ่งในนั้นคือการขยายตัวรั้ง

มีการขยายรั้งสองประเภทพื้นฐาน:

  • วงเล็บปีกการายชื่ออาจมีรายการของสตริงที่กำหนดเองโดยคั่นด้วยเครื่องหมายจุลภาค (รวมถึงรายการที่ซ้ำกันและสตริงว่าง) ตัวอย่างเช่น{a,b,c,,pp,cg,pp,}จะขยายไปยังa b c pp cg pp(สังเกตช่องว่างรอบ ๆ สตริงว่าง)
  • ..วงเล็บลำดับอาจมีปลายทางลำดับแยกจากกันโดย อีกทางเลือกหนึ่ง..อาจตามมาตามด้วยขนาดขั้นตอน จุดสิ้นสุดของลำดับอาจเป็นจำนวนเต็มหรืออักขระ ลำดับจะขึ้นหรือลงโดยอัตโนมัติตามจุดสิ้นสุดที่ใหญ่กว่า ตัวอย่างเช่น:
    • {0..15} จะขยายเป็น 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    • {-10..-5} จะขยายเป็น -10 -9 -8 -7 -6 -5
    • {3..-6..2} จะขยายเป็น 3 1 -1 -3 -5
    • {a..f} จะขยายเป็น a b c d e f
    • {Z..P..3} จะขยายเป็น Z W T Q

นอกเหนือจากนี้วงเล็บปีกกาลำดับและรายการอาจมีอยู่กับวงเล็บปีกการายการ:

  • {a,b,{f..k},p} จะขยายเป็น a b f g h i j k p
  • {a,{b,c}} จะขยายเป็น a b c

การจัดฟันขยายด้วยสตริงที่ไม่ใช่ช่องว่างทั้งสองด้าน ตัวอย่างเช่น:

  • c{a,o,ha,}t จะขยายเป็น cat cot chat ct

นอกจากนี้ยังใช้งานได้กับวงเล็บหลายตัวที่ต่อกัน:

  • {ab,fg}{1..3} จะขยายเป็น ab1 ab2 ab3 fg1 fg2 fg3

สิ่งนี้จะค่อนข้างซับซ้อน ตัวอย่างเช่น:

  • {A..C}{x,{ab,fg}{1..3},y,} จะขยายเป็น 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..c} {1..5} จะขยายเป็น a b c 1 2 3 4 5

สังเกตว่าคำสั่งซื้อจะถูกเก็บรักษาไว้อย่างไร


รายการสำหรับความท้าทายนี้จะขยายการขยาย bash brace ดังอธิบายข้างต้น โดยเฉพาะอย่างยิ่ง:

  • bashไม่อนุญาตให้ใช้eval โดย(หรือเชลล์อื่นที่มีการขยายตัวคล้ายกัน)
  • การจัดฟันแบบลำดับจะเป็นตัวเลขต่อจำนวนตัวพิมพ์เล็กถึงตัวพิมพ์เล็กหรือตัวพิมพ์ใหญ่ต่อตัวพิมพ์ใหญ่โดยไม่มีการผสม ตัวเลขจะเป็นจำนวนเต็มในช่วงเซ็นชื่อแบบ 32 บิต หากกำหนดขนาดขั้นตอนเพิ่มเติมจะเป็นจำนวนเต็มบวกเสมอ (โปรดทราบว่าการทุบตีจะขยาย{A..z}เช่นกัน แต่สิ่งนี้อาจถูกเพิกเฉยต่อความท้าทายนี้)
  • แต่ละรายการในวงเล็บปีกการายการจะประกอบด้วยตัวอักษรและตัวเลขตัวเล็กและตัวใหญ่เสมอ (รวมสตริงว่าง)
  • วงเล็บปีกการายการอาจมีการซ้อนโดยพลการของการขยายรั้งอื่น ๆ
  • การจัดฟันอาจถูกตัดแบ่งจำนวนครั้งโดยพลการ สิ่งนี้จะถูก จำกัด ด้วยหน่วยความจำภาษาของคุณดังนั้นความคาดหวังก็คือคุณสามารถทำการต่อหมายเลขโดยพลการของการต่อข้อมูลได้ แต่ถ้าหาก / เมื่อคุณมีหน่วยความจำไม่เพียงพอที่จะไม่นับรวมกับคุณ

ตัวอย่างในข้อความด้านบนเป็นตัวอย่างการทดสอบ สรุปด้วยอินพุตแต่ละบรรทัดที่สอดคล้องกับบรรทัดเอาต์พุตเดียวกันนั่นคือ:

อินพุต

{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

เอาท์พุต

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
ฉันมองเข้าไปในนี้และมันเป็นความเจ็บปวดเพียงเพื่อแยกเพราะทุกกรณีขอบ :-(
Neil

คำตอบ:


3

Ruby, 405 403 401 400 ไบต์

นักปราชญ์ (Jamie Zawinski) เคยกล่าวไว้ว่า "บางคนเมื่อต้องเผชิญกับปัญหาคิดว่า 'ฉันรู้ฉันจะใช้สำนวนปกติ' ตอนนี้พวกเขามีปัญหาสองข้อ "

ฉันไม่คิดว่าฉันพอใจกับคำพูดนั้นจนกว่าฉันจะพยายามแก้ปัญหานี้ด้วย regex ซ้ำ ในขั้นต้นกรณี regex ดูเหมือนง่ายจนกระทั่งฉันต้องจัดการกับกรณีขอบที่เกี่ยวข้องกับตัวอักษรติดกับวงเล็บและจากนั้นฉันรู้ว่าฉันอยู่ในนรก

ต่อไปเรียกใช้ออนไลน์ได้ที่นี่พร้อมกรณีทดสอบ

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

Ungolfed:

->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 ไบต์

ว้าวนี่มันเหมือนกับการเล่นกอล์ฟรหัสในการท้าทายหนึ่งครั้ง!

ขอบคุณ @Neil ที่ทำให้แลมบ์ดาสั้นลง

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(' ',',')+'}'))

คำอธิบาย

  • b: คำนวณช่วงตามข้อกำหนด
  • e: ส่งคืนตำแหน่งของวงเล็บปีกกาปิดสุดแรกสุด ที่กล่าวย้ำ
  • m: แยกองค์ประกอบด้านนอกสุดบนเครื่องหมายจุลภาค ซ้ำ
  • n: รวมอาร์เรย์ในขณะที่ตรวจสอบความว่างเปล่า ฉันand/orไปทำงานไม่ได้
  • p: งานส่วนใหญ่เสร็จแล้ว ตรวจสอบเคสทั้งหมด (ช่วง, เพียงแค่รายการ, ต้องรวม) ซ้ำ
  • o: สิ่งที่ควรป้อน รูปแบบอินพุต / pเอาต์พุตไป

ฉันรู้สึกว่าฉันสามารถพัฒนาได้ในบางสถานที่ดังนั้นฉันจะพยายามตีกอล์ฟให้มากขึ้น นอกจากนี้ฉันควรใส่รายละเอียดเพิ่มเติมในคำอธิบาย


ฉันคาดว่า[c+d for c in a for d in b] or a or bจะทำงาน
Neil

2

JavaScript (Firefox 30-57), 465 427 425 ไบต์

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

การfชั่งน้ำหนักรุ่น ES6 ที่ 10 ไบต์พิเศษ:

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>

คำอธิบาย: เริ่มต้นโดยการเปลี่ยนช่องว่างเป็นเครื่องหมายจุลภาคและ{}ล้อมรอบสตริงทั้งหมดเพื่อความสอดคล้อง (ขอบคุณ @Blue สำหรับแนวคิด) จากนั้นค้นหาโครงสร้างทั้งหมด{..}และขยายไปยัง{,}โครงสร้าง ถัดไปใช้การสอบถามซ้ำเพื่อขยาย{,}โครงสร้างทั้งหมดจากภายในสู่ภายนอก ในที่สุดแทนที่เครื่องหมายจุลภาคทั้งหมดด้วยช่องว่าง

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.