Erweitern Sie eine Bash-Klammer-Erweiterung

20

Aus meist historischen Gründen ist Bash ein ziemliches Durcheinander von Syntax- und Programmierparadigmen - dies kann das Golfen umständlich und manchmal frustrierend machen. Allerdings hat es ein paar Tricks im Ärmel, die es häufig mit anderen Mainstream-Skripten konkurrieren lassen Sprachen. Eine davon ist die Strebendehnung .

Es gibt zwei grundlegende Arten der Klammererweiterung:

  • Listenklammern können durch Kommas getrennte Listen beliebiger Zeichenfolgen enthalten (einschließlich Duplikate und der leeren Zeichenfolge). Zum Beispiel {a,b,c,,pp,cg,pp,}wird auf erweitert a b c pp cg pp(beachten Sie die Leerzeichen um die leeren Zeichenfolgen).
  • Sequenzklammern können durch getrennte Sequenzendpunkte enthalten ... Optional ..kann eine weitere folgen, gefolgt von einer Schrittgröße. Sequenzendpunkte können entweder Ganzzahlen oder Zeichen sein. Die Sequenz steigt automatisch auf oder ab, je nachdem welcher Endpunkt größer ist. Beispielsweise:
    • {0..15} wird zu erweitern 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    • {-10..-5} wird zu erweitern -10 -9 -8 -7 -6 -5
    • {3..-6..2} wird zu erweitern 3 1 -1 -3 -5
    • {a..f} wird zu erweitern a b c d e f
    • {Z..P..3} wird zu erweitern Z W T Q

Darüber hinaus können Sequenz- und Listenklammern mit Listenklammern vorhanden sein:

  • {a,b,{f..k},p} wird zu erweitern a b f g h i j k p
  • {a,{b,c}} wird zu erweitern a b c

Die geschweiften Klammern werden auf beiden Seiten durch Zeichenfolgen ohne Leerzeichen erweitert. Beispielsweise:

  • c{a,o,ha,}t wird zu erweitern cat cot chat ct

Dies funktioniert auch für mehrere miteinander verkettete geschweifte Klammern:

  • {ab,fg}{1..3} wird zu erweitern ab1 ab2 ab3 fg1 fg2 fg3

Dies kann sehr komplex werden. Beispielsweise:

  • {A..C}{x,{ab,fg}{1..3},y,} wird zu erweitern 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

Befindet sich zwischen den Erweiterungen jedoch ein Leerzeichen, werden sie einfach als separate Erweiterungen erweitert. Beispielsweise:

  • {a..c} {1..5} wird zu erweitern a b c 1 2 3 4 5

Beachten Sie, wie die Reihenfolge immer erhalten bleibt.


Einträge für diese Herausforderung erweitern die Bash-Klammer-Erweiterungen wie oben beschrieben. Bestimmtes:

  • eval by bash(oder andere Shells, die eine ähnliche Expansion durchführen) ist nicht erlaubt
  • Sequenzklammern werden immer von Nummer zu Nummer, von Kleinbuchstaben zu Kleinbuchstaben oder von Großbuchstaben zu Großbuchstaben ohne Vermischung angegeben. Zahlen sind ganze Zahlen im 32-Bit-Bereich mit Vorzeichen. Wenn angegeben, ist die optionale Schrittgröße immer eine positive ganze Zahl. (Beachten Sie, dass die Bash auch erweitert {A..z}wird, dies kann jedoch für diese Herausforderung ignoriert werden.)
  • Einzelne Elemente in geschweiften Klammern bestehen immer nur aus alphanumerischen Groß- und Kleinbuchstaben (einschließlich leerer Zeichenfolge).
  • Listenklammern können beliebige Schachtelungen anderer Klammererweiterungen enthalten
  • geschweifte Klammern können beliebig oft verkettet werden. Dies wird durch die Speicherkapazität Ihrer Sprache begrenzt. Sie können also theoretisch beliebig viele Verkettungen durchführen, wenn jedoch die Speicherkapazität erschöpft ist, zählt dies nicht für Sie.

Die Beispiele im obigen Text dienen als Testfälle. Zusammengefasst, wobei jede Eingabezeile derselben Ausgabezeile entspricht, sind sie:

Eingang

{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

Ausgabe

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
Digitales Trauma
quelle
3
Ich habe mir das angeschaut und es ist ein Schmerz, es einfach zu analysieren, wegen all der Randfälle :-(
Neil

Antworten:

3

Ruby, 405 403 401 400 Bytes

Ein weiser Mann (Jamie Zawinski) sagte einmal: "Manche Leute denken, wenn sie mit einem Problem konfrontiert werden, 'ich weiß, ich werde reguläre Ausdrücke verwenden.' Jetzt haben sie zwei Probleme. "

Ich glaube nicht, dass ich dieses Zitat voll und ganz geschätzt habe, bis ich versucht habe, dieses Problem mit rekursivem regulären Ausdruck zu lösen. Anfangs schienen die Regex-Fälle einfach zu sein, bis ich mich mit Randfällen befassen musste, in denen Buchstaben neben Klammern vorkamen, und dann wusste ich, dass ich in der Hölle war.

Wie auch immer, führen Sie es hier online mit Testfällen aus

->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
}
Wert Tinte
quelle
2

Python 2.7, 752 728 Bytes

Wow, das ist wie ein Haufen Code-Golf in einer Herausforderung!

Vielen Dank an @Neil für die Kürzung eines Lambdas

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

Erläuterung

  • b: berechnet den Bereich gemäß den Spezifikationen.
  • e: Gibt die Position der äußersten Klammer zurück. Iterativ.
  • m: teilt äußerste Elemente in Kommas auf. Rekursiv
  • n: Kombiniert Arrays, während nach Leergut gesucht wird. Ich konnte nicht and/orzur Arbeit gehen.
  • p: Wo die meiste Arbeit erledigt ist. Überprüft alle Fälle (Bereich, nur Liste, muss kombiniert werden). Rekursiv
  • o: Was sollte Eingang nehmen. Formate Eingabe / Ausgabe an p.

Ich habe das Gefühl, dass ich mich an einigen Stellen verbessern kann, also werde ich versuchen, mehr Golf zu spielen. Auch sollte ich in der Erklärung näher darauf eingehen.

Blau
quelle
Ich hätte erwartet [c+d for c in a for d in b] or a or bzu arbeiten.
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()}))

Die ES6-Version fwiegt zusätzlich 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>

Erläuterung: Beginnt damit, Leerzeichen in Kommas zu ändern und die gesamte Zeichenfolge {}aus Gründen der Konsistenz einzufügen (danke an @Blue für die Idee). Sucht dann nach allen {..}Konstrukten und erweitert sie zu {,}Konstrukten. Next verwendet die Rekursion, um alle {,}Konstrukte wiederholt von innen nach außen zu erweitern. Ersetzt schließlich alle Kommas durch Leerzeichen.

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
quelle