glob matcher 구현


15

일치시킬 패턴 및 문자열의 함수를 구현하고 패턴이 WHOLE 문자열과 일치하면 true를, 그렇지 않으면 false를 리턴하십시오.

우리의 glob 패턴 구문은 다음과 같습니다.

  • ? 하나의 문자와 일치
  • + 하나 이상의 문자와 일치
  • * 0 개 이상의 문자와 일치
  • \ 탈출

규칙 :

  • 평가 없음, 정규 표현식으로 변환 없음, 시스템 glob 함수 호출 없음.
  • I / O가 필요하지 않습니다 : 함수를 작성하기 만하면됩니다
  • 최단 승리

예 :

glob('abc', 'abc') => true
glob('abc', 'abcdef') => false IMPORTANT!
glob('a??', 'aww') => true
glob('a*b', 'ab') => true
glob('a*b', 'agwijgwbgioeb') => true
glob('a*?', 'a') => false
glob('?*', 'def') => true
glob('5+', '5ggggg') => true
glob('+', '') => false
glob('a\*b', 'a*b') => true

시작하는 방법은 다음과 같습니다. http://en.wikipedia.org/wiki/Backtracking


1
추가 태그 "패턴 일치"를 제안 할 수 있습니까?
dmckee --- 전 운영자 고양이

1
"표준 기능 없음"의 의미를 명확하게 설명해 주시겠습니까? 표준 라이브러리에서 함수를 호출 할 수 없습니까? 어떻게 작동합니까?
sepp2k

탈출에 대한 몇 가지 예? ( "\")
Eelvex

답변:


4

골프 스크립트-82 자

{1,\@n+:|;{:<;{:I)I|="\\+*?"[<]+?[{|=<={I))}*}I~{I\C}{}.{;}]=~}:C%}/{|>'*'-n=},}:g

문자열에 줄 바꿈이 없다고 가정합니다. false에 대해서는 빈 배열을, true에 대해서는 비어 있지 않은 배열을 반환합니다 (골프 스크립트 정의가 true / false 인 경우).

이것은 비 재귀 (연속 제외 해결책 *패턴 문자열의 위치의리스트를 유지 S) i되도록 pattern[0..i]일치 string[0..cur].

이것은 매우 오랫동안 실행될 가능성이 있습니다. 이를 방지하기 위해 .&이후 :C%에 추가 할 수 있습니다 .


5

하스켈, 141 자

c('\\':a:z)s=a&s>>=c z
c(a:z)s=a%s>>=c z
c[]s=[null s]
p&(a:z)|a==p=[z]
_&_=[]
'?'%(a:z)=[z]
'*'%a=a:'+'%a
'+'%(a:z)='*'%z
l%a=l&a
g=(or.).c

패턴과 문자열 모두 일치하는 모든 입력에 작동합니다. 패턴에서 후행 백 슬래시를 리터럴 일치로 처리합니다 (동작은 지정되지 않았습니다).

다음 테스트 드라이버를 사용하여 실행할 수 있습니다.

main = do
    globtest "abc" "abc"    True
    globtest "abc" "abcdef" False
    globtest "a??" "aww"    True
    globtest "a*b" "ab"     True
    globtest "a*b" "agwijgwbgioeb" True
    globtest "a*?" "a"      False
    globtest "?*" "def"     True
    globtest "5+" "5ggggg"  True
    globtest "+" ""         False
    globtest "a\\*b" "a*b"  True
  where
    globtest p s e =
      if g p s == e
        then putStrLn "pass"
        else putStrLn$"fail: g " ++ show p ++ " " ++ show s ++ " /= " ++ show e

업데이트 : Haskell이 문제를 쉽게 인코딩하는 방법을 잘 보여주기 때문에이 특정 답변에 대한 블로그 게시물을 작성했습니다 .


  • 편집 : (181-> 174) 대체 dm연산자
  • 편집 (174 -> 151) 인라인 r으로c
  • 편집 (151 -> 149)는 불필요에서 생성 옵션 제거 +케이스
  • 편집 : (149-> 141) %은에 대해 처리 된 불필요한 절을 제거 했습니다.&

2

PHP - 275 243 자

<?function g($P,$I){$o='array_shift';if(@$I[0]==="")return 0;for(;$P;$o($P)){$p=$P[0];if($p=='?'|$p=='+'&&@$N===$o($I))return 0;if($p=='+'|$p=='*'&&$I&&g($P,array_slice($I,1)))return 1;if(!strpos(" ?+*\\",$p)&&$p!==$o($I))return 0;}return!$I;}

언 골프 드 :

<?php

function g($P,$I) {
        if ($I && $I[0] === "") return false;
        for(;$P;array_shift($P)) {
                $p = $P[0];
                if( $p == '?' || $p == '+') {
                        if (NULL === array_shift($I)) {
                                return false;
                        }
                }
                if( $p=='+' || $p=='*' ) {
                        if ($I && g($P, array_slice($I,1))) {
                                return true;
                        }
                }
                if (!strpos(" ?+*\\",$p) && $p !== array_shift($I)) {
                        return false;
                }
        }
        return !$I;
}

function my_glob($pattern,$subject) {
    return !!g(str_split($pattern),str_split($subject));
}

2

지나치게 자세한 정보 파이썬 ( 384 개 367 문자)

t=lambda a:a[1:] 
h=lambda a:a[0] 
n=lambda p,s:s and(h(p)==h(s)and m(t(p),t(s))) 
def m(p,s): 
 if not p: 
  return not s 
 else: 
  return { 
   '?':lambda p,s:s and m(t(p),t(s)), 
   '+':lambda p,s:s and(m(p,t(s))or m(t(p),t(s))), 
   '*':lambda p,s:m(t(p),s)or(s and m(p,t(s))), 
   '\\':lambda p,s:n(t(p),s), 
  }.get(h(p),n)(p,s) 
glob=lambda p,s:not not m(p,s)

가장 짧지는 않지만 훌륭하고 기능적입니다. 중간에 파견 명령이 (h(p) == '?') and (? lambda body)유형에 대한 분리로 다시 쓰여질 수 있습니다 . h 연산자를 정의하면 아무런 비용이 들지 않지만 일부 키워드가 필요하지만 head 키워드를 사용하는 것이 좋습니다.

시간이 허락한다면 나중에 골프 스크립팅에 금이 가고 싶습니다.

편집 : user300의 루비 답변을 읽은 후 '*'경우 불필요한 세 번째 분기가 제거되었습니다.


2

짧은 Snappier Python 2.6 (272 자)

골프 :

n=lambda p,s:p[0]==s[0]and m(p[1:],s[1:]) 
def m(p,s): 
 q,r,t,u=p[0],p[1:],s[0],s[1:] 
 return any((q=='?'and(t and m(r,u)),q=='+'and(t and(m(p,u)or m(r,u))),q=='*'and(m(r,s)or(t and m(p,u))),q=='\\'and n(r,s),q==t==0))or n(p,s) 
glob=lambda*a:m(*[list(x)+[0]for x in a])

언 골프 :

TERMINATOR = 0 

def unpack(a): 
    return a[0], a[1:] 

def terminated_string(s): 
    return list(s) + [TERMINATOR] 

def match_literal(p, s): 
    p_head, p_tail = unpack(p) 
    s_head, s_tail = unpack(s) 
    return p_head == s_head and match(p_tail, s_tail) 

def match(p, s): 
    p_head, p_tail = unpack(p) 
    s_head, s_tail = unpack(s) 
    return any(( 
        p_head == '?' and (s_head and match(p_tail, s_tail)), 
        p_head == '+' and (s_head and(match(p, s_tail) or match(p_tail, s_tail))), 
        p_head == '*' and (match(p_tail, s) or (s_head and match(p, s_tail))), 
        p_head == '\\' and match_literal(p_tail, s), 
        p_head == s_head == TERMINATOR, 
    )) or match_literal(p, s) 

def glob(p, s): 
    return match(terminated_string(p), terminated_string(s))

특징 :

  • 지연 평가 논리 혼란!
  • C 스타일 문자열!
  • 귀여운 다중 비교 관용구!
  • 못생긴 많이!

빈 문자열에서 머리를 튀길 때 터미네이터 값을 얻을 수있는 경우 상황이 단순화되는 방법을 설명하는 user300의 답변에 대한 공로.

m / 인수를 선언하는 동안 헤드 / 테일 포장 풀기를 인라인으로 수행 할 수 있기를 바랍니다. m은 친구 n과 glob처럼 람다가 될 수 있습니다. python2는 그것을 할 수 없으며 약간의 독서 후에 python3도 할 수없는 것처럼 보입니다. 비애.

테스트 :

test_cases = { 
    ('abc', 'abc') : True, 
    ('abc', 'abcdef') : False, 
    ('a??', 'aww') : True, 
    ('a*b', 'ab') : True, 
    ('a*b', 'aqwghfkjdfgshkfsfddsobbob') : True, 
    ('a*?', 'a') : False, 
    ('?*', 'def') : True, 
    ('5+', '5ggggg') : True, 
    ('+', '') : False, 
}   
for (p, s) in test_cases: 
    computed_result = glob(p, s) 
    desired_result = test_cases[(p, s)] 
    print '%s %s' % (p, s) 
    print '\tPASS' if (computed_result == desired_result) else '\tFAIL' 

2

루비 - 199 (171)

g=->p,s{x=(b=->a{a[1..-1]})[p];y=s[0];w=b[s];v=p[0];_=->p,s{p[0]==y&&g[x,w]}
v==??? g[x,y&&w||s]:v==?+? y&&g[?*+x,w]:v==?*?
y&&g[p,w]||g[x,s]:v==?\\? _[x,s]:v ? _[p,s]:!y}

언 골프 드 :

def glob(pattern, subject)
        b=->a{a[1..-1]}
        _=->p,s{ p[0]==s[0] && glob(b[p],b[s]) }
        ({
                ??=>->p,s { glob(b[p], s[0] ? b[s] : s) },
                ?+=>->p,s { s[0] && glob(?*+b[p], b[s]) },
                ?*=>->p,s { s[0] && glob(p,b[s]) || glob(b[p],s) },
                ?\\=>->p,s{ _[b[p],s] },
                nil=>->p,s{ !subject[0] }
        }[pattern[0]] || _)[pattern, subject]
end

테스트 :

p glob('abc', 'abc')
p glob('abc', 'abcdef')
p glob('a??', 'aww')
p glob('a*b', 'ab')
p glob('a*b', 'agwijgwbgioeb')
p glob('a*?', 'a')
p glob('?*', 'def')
p glob('5+', '5ggggg')
p glob('+', '')

roobs의 답변에서 영감을 받음


루비에 대해서는 아무것도 모르지만 코드에서 범위를 벗어난 인덱스에 액세스하면 nil이 반환된다는 것을 알게되었습니다. 따라서 빈 문자열을 팝하면 문자열 종결 기호로 사용할 수있는 nil 값이 생성됩니다. C 스타일! 맵시 있는! 각 입력 문자열을 통해 파이썬에서 흉내낼 수 있다고 생각합니다.lambda s : list(s)+[None]
roobs

외관상 Ruby는 패턴 일치 기능을 내장하고 있습니다. 그것은 이런 종류의 문제에 확실히 편리합니다.
Jonathan M Davis

실제로는 ??문자 문자는,이다 =>루비 해시 키 / 값 분리기하고, ->람다 :-) (시작 { ?? => ->{...} }키와 해시 "?"와 같은 값. 람다) 그러나 예 방법 단일 문자의 패턴 매칭 등의 중고 함께 외모 :-)
Arnaud Le Blanc 10

2

C 함수-178 개의 필수 문자

GCC로 컴파일하면 경고가 발생하지 않습니다.

#define g glob
int g(p,s)const char*p,*s;{return*p==42?g(p+1,s)||(*s&&g(p,
s+1)):*p==43?*s&&(g(p+1,++s)||g(p,s)):*p==63?*s&&g(p+1,s+1)
:*p==92?*++p&&*s++==*p++&&g(p,s):*s==*p++&&(!*s++||g(p,s));}
#undef g

첫 줄과 마지막 줄은 문자 수에 포함되지 않습니다. 편의상 제공됩니다.

폭파 :

int glob(p,s)
const char *p, *s; /* K&R-style function declaration */
{
    return
        *p=='*'  ? glob(p+1,s) || (*s && glob(p,s+1)) :
        *p=='+'  ? *s && (glob(p+1,++s) || glob(p,s)) :
        *p=='?'  ? *s && glob(p+1,s+1)                :
        *p=='\\' ? *++p && *s++==*p++ && glob(p,s)    :
        *s==*p++ && (!*s++ || glob(p,s));
}

2

자바 스크립트-259 자

내 구현은 매우 재귀 적이므로 매우 긴 패턴을 사용하면 스택이 오버플로됩니다. 더하기 부호 (최적화 할 수는 있지만 단순성을 위해 선택하지 않은)를 무시하면 각 토큰마다 한 수준의 재귀가 사용됩니다.

glob=function f(e,c){var b=e[0],d=e.slice(1),g=c.length;if(b=="+")return f("?*"+d,c);if(b=="?")b=g;else if(b=="*"){for(b=0;b<=g;++b)if(f(d,c.slice(b)))return 1;return 0}else{if(b=="\\"){b=e[1];d=e.slice(2)}b=b==c[0]}return b&&(!d.length&&!g||f(d,c.slice(1)))}

이 함수는 때때로 부울 대신 숫자를 반환합니다. 그것이 문제라면,로 사용할 수 있습니다 !!glob(pattern, str).


유용한 리소스로 사용하기위한 Ungolfed (최소화되지 않음) :

function glob(pattern, str) {
    var head = pattern[0], tail = pattern.slice(1), strLen = str.length, matched;
    if(head == '+') {
        // The plus is really just syntactic sugar.
        return glob('?*' + tail, str);
    }
    if(head == '?') { // Match any single character
        matched = strLen;
    } else if(head == '*') { // Match zero or more characters.
        // N.B. I reuse the variable matched to save space.
        for(matched = 0; matched <= strLen; ++matched) {
            if(glob(tail, str.slice(matched))) {
                return 1;
            }
        }
        return 0;
    } else { // Match a literal character
        if(head == '\\') { // Handle escaping
            head = pattern[1];
            tail = pattern.slice(2);
        }
        matched = head == str[0];
    }
    return matched && ((!tail.length && !strLen) || glob(tail, str.slice(1)));
}

배열 요소와 같이 문자열의 문자로 인덱싱하는 것은 이전 (ECMAScript 3) 언어 표준의 일부가 아니므로 이전 브라우저에서는 작동하지 않을 수 있습니다.


1

파이썬 (454 자)

def glob(p,s):
  ps,pns=[0],[]
  for ch in s:
    for i in ps:
      if i<0:
        pns+=[i]
        if i>-len(p) and p[-i]==ch:pns+=[-i]
      elif i<len(p):
        pc=p[i]
        d={'?':[i+1],'+':[i,-i-1],'*':[i+1,-i-1]}
        if pc in d:pns+=d[pc]
        else:
          if pc=='\\':pc=p[i+1]
          if pc==ch:pns+=[i+1]
    ps,pns=pns,[]
  if (s or p in '*') and (len(p) in ps or -len(p)+1 in ps or -len(p) in ps): return True
  return False

1

D : 363 자

bool glob(S)(S s,S t){alias front f;alias popFront p;alias empty e;while(!e(s)&&!e(t)){switch(f(s)){case'+':if(e(t))return false;p(t);case'*':p(s);if(e(s))return true;if(f(s)!='+'&&f(s)!='*'){for(;!e(t);p(t)){if(f(s)==f(t)&&glob(s,t))return true;}}break;case'\\':p(s);if(e(s))return false;default:if(f(s)!=f(s))return false;case'?':p(s);p(t);}}return e(s)&&e(t);}

더 읽기 :

bool glob(S)(S s, S t)
{
    alias front f;
    alias popFront p;
    alias empty e;

    while(!e(s) && !e(t))
    {
        switch(f(s))
        {
            case '+':
                if(e(t))
                    return false;

                p(t);
            case '*':
                p(s);

                if(e(s))
                    return true;

                if(f(s) != '+' && f(s) != '*')
                {
                    for(; !e(t); p(t))
                    {
                        if(f(s) == f(t) && glob(s, t))
                            return true;
                    }
                }

                break;
            case '\\':
                p(s);

                if(e(s))
                    return false;
            default:
                if(f(s) != f(s))
                    return false;
            case '?':
                p(s);
                p(t);
        }
    }

    return e(s) && e(t);
}

1

골프 스크립트

{{;;}2$+}:x;{x if}:a;{x\if}:o;{1$1$}:b;{(@(@={\m}a}:r;{b(63={\({\m}a}a{b(43={\({\b m{'+'\+m}o}a}a{b(42={b m{\({\'*'\+m}a}o}a{b(92={r}a{b 0=0=\0=0=*{r}o}o}o}o}o}:m;{[0]+\[0]+m}:glob;

스택에서 s와 p라는 두 개의 인수를 사용하고 단일 부울 리턴 값을 생성하는 함수로 만들어졌습니다. 게으른 및 게으른 또는 연산자와 호환되도록 약간의 혼란이 있습니다. 나는이 접근법이 최적의 위치 또는 올바른 방향에 있는지 의심합니다.

'*'패턴을 터 뜨리고 '*'비교하는 데 소비하는 것과 같은 재미있는 바보 같은 순간도 있습니다 . 다른 브랜치를 내려 가려면 '*'앞에 있는 패턴이 필요 하지만을 터 뜨렸을 때 원래 패턴 '*'을 소비하고을 소비 '*'했으므로 패턴을 다시 얻으려면 반짝이는 새 문자열을로드합니다. constant '*', 앞에 붙입니다. 어떤 이유로 든 문자 일치를 ASCII 값으로 수행해야하지만 문자열 앞에 다시 문자열이 필요하기 때문에 더 나빠집니다.

덜 골프 골프 스크립트

{[0]+}:terminate_string;
{{;;}2$+if}:_and;
{{;;}2$+\if}:_or;
{1$1$}:branch;
{(@(@={\match}_and}:match_literal;
{0=0=\0=0=*}:match_terminator;
{(92={match_literal}_and}:match_escape;
{(63={\({\match}_and}_and}:match_wildcard;
{(43={\({\branch match{'+'\+match}_or}_and}_and}:match_wildcard_plus;
{(42={branch match{\({\'*'\+match}_and}_or}_and}:match_wildcard_star;
{branch match_wildcard{branch match_wildcard_plus{branch match_wildcard_star{branch match_escape{branch match_terminator{match_literal}_or}_or}_or}_or}_or}:match;
{terminate_string\terminate_string match}:glob;

테스트

{2$2$glob = "test passed: " "test FAILED: " if print \ print ' ; ' print print "\n" print}:test_case;

'abc' 'abc' 1 test_case
'abc' 'abcdef' 0 test_case
'a??' 'aww' 1 test_case
'a*b' 'ab' 1 test_case
'a*b' 'agwijgwbgioeb' 1 test_case
'a*?' 'a' 0 test_case
'?*' 'def' 1 test_case
'5+' '5ggggg' 1 test_case
'+' '' 0 test_case

1

C # (251 자)

static bool g(string p,string i){try{char c;System.Func<string,string>s=t=>t.Remove(0,1);return p==i||((c=p[0])==92?p[1]==i[0]&g(s(s(p)),s(i)):c==42?g(s(p),i)||g(p,s(i)):c==43?g(s(p),s(i))|g(p,s(i)):g(s(p),s(i))&(c==i[0]|c==63));}catch{return false;}}

약간 더 읽기 쉽다 :

static bool g(string p /* pattern */, string i /* input string */)
{
    // Instead of checking whether we’ve reached the end of the string, just
    // catch the out-of-range exception thrown by the string indexing operator
    try
    {
        char c;

        // .Remove(0,1) is shorter than .Substring(1)...
        System.Func<string, string> s = t => t.Remove(0, 1);

        // Note that every glob matches itself!† This saves us having to write
        // “(p=="" & i=="")” which would be much longer — very convenient!
        return p == i || (

            // backslash escapes
            (c = p[0]) == 92 ? p[1] == i[0] & g(s(s(p)), s(i)) :

            // '*' — need “||” so that s(i) doesn’t throw if the first part is true
            c == 42 ? g(s(p), i) || g(p, s(i)) :

            // '+'
            c == 43 ? g(s(p), s(i)) | g(p, s(i)) :

            // '?' or any other character
            g(s(p), s(i)) & (c == i[0] | c == 63)
        );
    }

    // If we ever access beyond the end of the string, we know the glob doesn’t match
    catch { return false; }
}

나는 알고있다. 나는 백 슬래시를 포함하는 globs를 제외하고. 정말 불행합니다. 그렇지 않으면 정말 영리했을 것입니다. :(

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.