Développer une extension d'accolade bash


20

Pour des raisons principalement historiques, bash est un méli-mélo de paradigmes de syntaxe et de programmation - cela peut le rendre gênant et parfois frustrant pour le golf. Cependant, il a quelques astuces dans sa manche qui peuvent souvent le rendre compétitif avec d'autres scripts traditionnels. langues. L'un d'eux est l' expansion des orthèses .

Il existe deux types de base d'extension d'expansion:

  • Les accolades de liste peuvent contenir des listes de chaînes arbitraires séparées par des virgules (y compris les doublons et la chaîne vide). Par exemple {a,b,c,,pp,cg,pp,}, développera a b c pp cg pp(notez les espaces autour des chaînes vides).
  • Les accolades de séquence peuvent contenir des points de terminaison de séquence séparés par ... Facultativement, un autre ..peut suivre, suivi d'une taille de pas. Les points de terminaison de séquence peuvent être des nombres entiers ou des caractères. La séquence monte ou descend automatiquement en fonction du point final le plus élevé. Par exemple:
    • {0..15} va s'étendre à 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    • {-10..-5} va s'étendre à -10 -9 -8 -7 -6 -5
    • {3..-6..2} va s'étendre à 3 1 -1 -3 -5
    • {a..f} va s'étendre à a b c d e f
    • {Z..P..3} va s'étendre à Z W T Q

Au-delà, des accolades de séquence et de liste peuvent exister avec des accolades de liste:

  • {a,b,{f..k},p} va s'étendre à a b f g h i j k p
  • {a,{b,c}} va s'étendre à a b c

Les accolades se développent avec des chaînes non blanches de chaque côté. Par exemple:

  • c{a,o,ha,}t va s'étendre à cat cot chat ct

Cela fonctionne également pour plusieurs accolades concaténées ensemble:

  • {ab,fg}{1..3} va s'étendre à ab1 ab2 ab3 fg1 fg2 fg3

Cela peut devenir assez complexe. Par exemple:

  • {A..C}{x,{ab,fg}{1..3},y,} va s'étendre à 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

Cependant, s'il y a un espace entre les extensions, elles se développent simplement en tant qu'extensions distinctes. Par exemple:

  • {a..c} {1..5} va s'étendre à a b c 1 2 3 4 5

Notez comment l'ordre est toujours préservé.


Les entrées pour ce défi développeront les extensions d'accolade bash comme décrit ci-dessus. En particulier:

  • eval by bash(ou d'autres shells qui effectuent une expansion similaire) n'est pas autorisé
  • les accolades de séquence seront toujours numérotées, minuscules à minuscules ou majuscules à majuscules sans mélange. Les nombres seront des entiers dans la plage signée 32 bits. Si elle est donnée, la taille de pas facultative sera toujours un entier positif. (Notez que bash se développera {A..z}également, mais cela peut être ignoré pour ce défi)
  • les éléments individuels entre accolades de liste seront toujours composés uniquement de caractères alphanumériques en majuscules et minuscules (chaîne vide incluse)
  • les accolades de liste peuvent contenir des imbrications arbitraires d'autres extensions d'accolade
  • les accolades peuvent être concaténées un nombre arbitraire de fois. Ceci sera limité par la mémoire de votre langue, donc on s'attend à ce que vous puissiez théoriquement faire un nombre arbitraire de concaténations mais si / quand vous manquez de mémoire cela ne comptera pas contre vous.

Les exemples dans le texte ci-dessus servent de cas de test. Résumées, chaque ligne d'entrée correspondant à la même ligne de sortie, elles sont:

Contribution

{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

Production

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
J'ai regardé cela et c'est une douleur simplement à analyser à cause de tous les cas de bord :-(
Neil

Réponses:


3

Ruby, 405 403 401 400 octets

Un homme sage (Jamie Zawinski) a dit un jour: "Certaines personnes, confrontées à un problème, pensent" je sais, je vais utiliser des expressions régulières ". Maintenant, ils ont deux problèmes."

Je ne pense pas avoir pleinement apprécié cette citation jusqu'à ce que j'essaie de résoudre ce problème avec l'expression régulière récursive. Initialement, les cas d'expression régulière semblaient simples, jusqu'à ce que je doive m'occuper des cas marginaux impliquant des lettres adjacentes à des crochets, puis je savais que j'étais en enfer.

Quoi qu'il en soit, exécutez-le en ligne ici avec des cas de 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)*' '}

Non golfé:

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

Wow, c'est comme un tas de golfs de code dans un défi!

Merci à @Neil d'avoir raccourci une 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(' ',',')+'}'))

Explication

  • b: calcule la portée en fonction des spécifications.
  • e: retourne la position du premier croisillon le plus à l'extérieur. Itératif.
  • m: divise les éléments les plus à l'extérieur par des virgules. Récursif.
  • n: combine des tableaux tout en vérifiant les vides. Je ne pouvais pas aller and/ortravailler.
  • p: Où la plupart du travail est effectué. Vérifie tous les cas (plage, liste juste, doit être combinée). Récursif.
  • o: Qu'est-ce qui devrait prendre une entrée. Formate les entrées / sorties dans p.

Je sens que je peux m'améliorer dans certains endroits, alors j'essaierai de jouer davantage au golf. Je devrais également mettre plus en détail dans l'explication.


Je m'attendais [c+d for c in a for d in b] or a or bà travailler.
Neil

2

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

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 version ES6 fpèse 10 octets supplémentaires:

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>

Explication: Commence par changer les espaces en virgules et en encapsulant la chaîne entière {}pour plus de cohérence (merci à @Blue pour l'idée). Recherche ensuite toutes les {..}constructions et les développe en {,}constructions. La récursivité suivante utilise pour développer à plusieurs reprises toutes les {,}constructions de l'intérieur vers l'extérieur. Remplace enfin toutes les virgules par des espaces.

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.