Розгорніть розширення баш-дужки


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, як описано вище. Зокрема:

  • eval bash(або іншими оболонками, які виконують подібне розширення) не дозволяється
  • Брекети послідовності завжди будуть числоми до числа, малими літерами або великими літерами або великими літерами без змішування. Числа будуть цілими числами в 32-бітному діапазоні, підписаному. Якщо дано, необов'язковий розмір кроку завжди буде додатним цілим числом. (Зверніть увагу, що bash також буде розширюватися {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
Я переглянув це, і болісно просто розібратися через усі крайові випадки :-(
Ніл

Відповіді:


3

Рубін, 405 403 401 400 байт

Мудрець (Джеймі Завінський) одного разу сказав: "Деякі люди, стикаючись з проблемою, думають:" Я знаю, я буду використовувати регулярні вирази ". Зараз у них дві проблеми ".

Я не думаю, що я цілком оцінив цю цитату, поки не намагався вирішити цю проблему рекурсивним регулярним виразом. Спочатку випадки регулярних виразів здавалися простими, поки мені не довелося мати справу з крайовими справами, що стосуються букв, прилеглих до дужок, і тоді я зрозумів, що перебуваю в пеклі.

У будь-якому випадку, запустіть його в Інтернеті за допомогою тестових випадків

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

Безголівки:

->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працювати.
Ніл

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

Версія ES6 fважить додатково 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.