bash 괄호 확장 펼치기


20

대부분의 역사적 이유에서 bash는 구문과 프로그래밍 패러다임의 절묘한 피지입니다. 이로 인해 어색하고 때로는 골프에 좌절감을 줄 수 있습니다. 언어. 이 중 하나는 괄호 확장 입니다.

괄호 확장에는 두 가지 기본 유형이 있습니다.

  • 목록 중괄호에는 쉼표로 구분 된 임의 문자열 (중복 및 빈 문자열 포함) 목록이 포함될 수 있습니다. 예를 들어 (빈 문자열 주위의 공백에 유의)로 {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 괄호 확장을 확장합니다. 특히:

  • 에 의해 평가 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
나는 이것을 조사하고 모든 중요한 경우 때문에 단순히 파싱하는 것은 고통이다 :-(
Neil

답변:


3

루비 405 403 401 400 바이트

한 현명한 사람 (Jamie Zawinski)은 "어떤 사람들은 문제에 직면 할 때 '나는 정규식을 사용할 것입니다.'라고 생각합니다. 이제 두 가지 문제가 있습니다. "

재귀 정규 표현식 으로이 문제를 해결하려고 할 때까지 나는 그 인용문을 완전히 이해하지 못했다고 생각합니다. 처음에는 대괄호 옆에있는 문자가 포함 된 가장 중요한 경우를 다루어야 할 때까지 정규식 사례가 단순 해 보였고 나는 지옥에 있다는 것을 알았습니다.

어쨌든 테스트 사례와 함께 여기에서 온라인으로 실행하십시오.

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

파이썬 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

자바 스크립트 (파이어 폭스 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.