Espandere un'espansione bash brace


20

Per motivi prevalentemente storici, bash è un vero miscuglio di sintassi e paradigmi di programmazione - questo può renderlo imbarazzante e talvolta frustrante per giocare a golf. Tuttavia ha qualche asso nella manica che spesso può renderlo competitivo con altri script tradizionali le lingue. Uno di questi è l' espansione del tutore .

Esistono due tipi base di espansione del controvento:

  • Le parentesi graffe elenco possono contenere elenchi separati da virgole di stringhe arbitrarie (inclusi duplicati e stringa vuota). Ad esempio {a,b,c,,pp,cg,pp,}si espanderà a a b c pp cg pp(notare gli spazi attorno alle stringhe vuote).
  • Le parentesi graffe di sequenza possono contenere punti finali separati da ... Opzionalmente ..può seguirne un altro , seguito da una dimensione del gradino. Gli endpoint della sequenza possono essere numeri interi o caratteri. La sequenza salirà o scenderà automaticamente in base a quale endpoint è maggiore. Per esempio:
    • {0..15} si espanderà a 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    • {-10..-5} si espanderà a -10 -9 -8 -7 -6 -5
    • {3..-6..2} si espanderà a 3 1 -1 -3 -5
    • {a..f} si espanderà a a b c d e f
    • {Z..P..3} si espanderà a Z W T Q

Oltre a ciò, possono esistere parentesi graffe di sequenza ed elenco con parentesi graffe di elenco:

  • {a,b,{f..k},p} si espanderà a a b f g h i j k p
  • {a,{b,c}} si espanderà a a b c

Le parentesi graffe si espandono con stringhe non bianche su entrambi i lati. Per esempio:

  • c{a,o,ha,}t si espanderà a cat cot chat ct

Questo funziona anche con più apparecchi concatenati insieme:

  • {ab,fg}{1..3} si espanderà a ab1 ab2 ab3 fg1 fg2 fg3

Questo può diventare piuttosto complesso. Per esempio:

  • {A..C}{x,{ab,fg}{1..3},y,} si espanderà a 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

Tuttavia, se c'è spazio tra le espansioni, allora si espandono semplicemente come espansioni separate. Per esempio:

  • {a..c} {1..5} si espanderà a a b c 1 2 3 4 5

Nota come l'ordine viene sempre preservato.


Le voci per questa sfida espanderanno le espansioni di parentesi graffe come descritto sopra. In particolare:

  • bashnon è consentito valutare da (o altre shell che eseguono un'espansione simile)
  • le parentesi graffe di sequenza saranno sempre da numero a numero, da minuscole a minuscole o da maiuscole a maiuscole senza miscelazione. I numeri saranno numeri interi nell'intervallo con segno a 32 bit. Se indicato, la dimensione del passaggio opzionale sarà sempre un numero intero positivo. (Nota che anche bash si espanderà {A..z}, ma questo può essere ignorato per questa sfida)
  • i singoli elementi tra parentesi graffe saranno sempre composti solo da caratteri alfanumerici maiuscoli e minuscoli (stringa vuota inclusa)
  • le parentesi graffe possono contenere annidamenti arbitrari di altre espansioni di parentesi graffe
  • le parentesi graffe possono essere concatenate un numero arbitrario di volte. Questo sarà limitato dalla memoria del tuo linguaggio, quindi l'aspettativa è che teoricamente puoi fare un numero arbitrario di concatenazioni, ma se / quando esaurisci la memoria che non conterà contro di te.

Gli esempi nel testo sopra servono come testcase. Riassumendo, con ogni linea di input corrispondente alla stessa linea di output, sono:

Ingresso

{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

Produzione

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
Ho esaminato questo ed è un dolore semplicemente analizzare a causa di tutti i casi limite :-(
Neil

Risposte:


3

Rubino, 405 403 401 400 byte

Un saggio (Jamie Zawinski) una volta disse: "Alcune persone, di fronte a un problema, pensano" Lo so, userò espressioni regolari ". Ora hanno due problemi ".

Non credo di aver apprezzato appieno quella citazione fino a quando non ho provato a risolvere questo problema con regex ricorsivo. Inizialmente, i casi di regex sembravano semplici, fino a quando non ho dovuto affrontare i casi limite relativi alle lettere adiacenti alle parentesi, e poi ho saputo che ero all'inferno.

Ad ogni modo, eseguilo online qui con casi di test

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

Wow, questo è come un sacco di golf di codice in una sfida!

Grazie a @Neil per abbreviare un 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(' ',',')+'}'))

Spiegazione

  • b: calcola l'intervallo in base alle specifiche.
  • e: restituisce la posizione della prima parentesi chiusa più esterna. Iterativo.
  • m: suddivide gli elementi più esterni sulle virgole. Ricorsivo.
  • n: combina array mentre verifica la presenza di vuoti. Non riuscivo and/ora lavorare.
  • p: Dove viene eseguita la maggior parte del lavoro. Controlla tutti i casi (intervallo, solo elenco, deve essere combinato). Ricorsivo.
  • o: Cosa dovrebbe richiedere input. Formatta input / output su p.

Sento di poter migliorare in alcuni luoghi, quindi cercherò di giocare a golf di più. Inoltre dovrei mettere più in dettaglio nella spiegazione.


Mi sarei aspettato [c+d for c in a for d in b] or a or bdi lavorare.
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()}))

La versione ES6 fpesa a 10 byte extra:

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>

Spiegazione: inizia cambiando gli spazi in virgole e avvolgendo l'intera stringa {}per coerenza (grazie a @Blue per l'idea). Quindi cerca tutti i {..}costrutti e li espande in {,}costrutti. Successivamente utilizza la ricorsione per espandere ripetutamente tutti i {,}costrutti dall'interno verso l'esterno. Sostituisce infine tutte le virgole con spazi.

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.