Zaimplementuj glob Matcher

15

Zaimplementuj funkcję wzorca i łańcucha do dopasowania, zwróć true, jeśli wzorzec pasuje do CAŁEGO łańcucha, w przeciwnym razie false.

Nasza składnia wzorca globalnego to:

  • ? pasuje do jednej postaci
  • + dopasowuje jeden lub więcej znaków
  • * dopasowuje zero lub więcej znaków
  • \ ucieka

Zasady:

  • Bez ewaluacji, bez konwersji na wyrażenie regularne, bez wywoływania systemowej funkcji glob.
  • I / O nie są wymagane: możesz po prostu napisać funkcję
  • Najkrótsze wygrane

Przykłady:

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

Oto wskazówka na początek: http://en.wikipedia.org/wiki/Backtracking

Ming-Tang
źródło
1
Czy mogę zasugerować dodatkowy tag „dopasowujący wzór”?
dmckee --- były moderator kociak
1
Czy możesz wyjaśnić, co rozumiesz przez „brak standardowej funkcji”? Czy nie możesz wywoływać funkcji ze standardowej biblioteki? Jak to ma działać?
sepp2k
Proszę podać kilka przykładów ucieczki? („\”)
Eelvex

Odpowiedzi:

4

Golfscript - 82 znaki

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

Zakłada, że ​​w ciągach nie ma żadnych znaków nowej linii. Zwraca pustą tablicę dla wartości false i niepustą tablicę dla wartości true (zgodnie z definicją true / false w golfscript).

Jest to rozwiązanie nierekurencyjne (z wyjątkiem kolejnych *s), które utrzymuje listę pozycji w łańcuchu wzorca i, które pattern[0..i]pasują string[0..cur].

Może to działać bardzo długo. Możesz dodać .&po, :C%aby temu zapobiec.

Nabb
źródło
5

Haskell, 141 znaków

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

Działa dla wszystkich danych wejściowych, zarówno wzorców, jak i ciągów znaków, z którymi należy się dopasować. Obsługuje końcowe ukośniki odwrotne we wzorcu jako dosłowne dopasowanie (zachowanie nie zostało określone).

Można to uruchomić za pomocą następującego sterownika testowego:

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

Aktualizacja: Napisałem post na blogu o tej konkretnej odpowiedzi, ponieważ myślę, że dobrze pokazuje, jak Haskell tak łatwo koduje problem.


  • Edycja: (181 -> 174) otrzymuje di mz operatorami
  • Edycja: (174 -> 151) wstawione rdoc
  • Edycja: (151 -> 149) usunął niepotrzebnie wygenerowaną opcję w +sprawie
  • Edycja: (149 -> 141) usunął niepotrzebną klauzulę dla %, którą obsłużył&
MtnViewMark
źródło
2

PHP - 275 243 znaków

<?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;}

Nie golfowany:

<?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));
}
Arnaud Le Blanc
źródło
2

Zbyt szczegółowy Python ( 384 367 znaków)

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)

Nie jest najkrótszy, ale jest miły i funkcjonalny. Środek dyktowania wysyłki w środku można przypuszczalnie przepisać jako rozbieżność w stosunku do (h(p) == '?') and (? lambda body)rzeczy typowych . Zdefiniowanie tego operatora h kosztuje mnie kilka znaków bez korzyści, ale miło jest mieć słowo kluczowe na głowę.

Chciałbym mieć crack do gry w golfa później, jeśli pozwoli na to czas.

edycja: usunięto niepotrzebną trzecią gałąź w przypadku „*” po przeczytaniu rubinowej odpowiedzi user300

roobs
źródło
2

Krótszy Snappier Python 2.6 (272 znaki)

grał w golfa:

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

bez golfa:

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

z udziałem:

  • leniwie oceniany logiczny bałagan!
  • Struny w stylu C!
  • ładny idiom wielokrotnego porównania!
  • dużo brzydkich!

podziękowania dla odpowiedzi użytkownika 300 za zilustrowanie, jak upraszcza się sprawy, jeśli można uzyskać jakąś wartość terminatora podczas wyskakiwania z pustego łańcucha.

Chciałbym, aby rozpakowanie głowy / ogona mogło odbywać się w linii podczas deklaracji argumentów m. wtedy m może być lambda, tak jak jego przyjaciele n i glob. python2 nie może tego zrobić, a po krótkiej lekturze wygląda na to, że python3 też nie może. Biada.

testowanie:

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' 
roobs
źródło
2

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

Nie golfowany:

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

Testy:

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

Zainspirowany odpowiedzią roobs

Arnaud Le Blanc
źródło
nic nie wiem o ruby, ale z twojego kodu dowiedziałem się, że dostęp do indeksów poza granicami zwraca zero. więc usunięcie pustego łańcucha daje wartość zerową, która może być użyta jako symbol terminatora łańcucha. W stylu C! fajne! myślę, że można to naśladować w pythonie, przekazując każdy ciąg wejściowy przezlambda s : list(s)+[None]
roobs
Wygląda na to, że Ruby ma wbudowane dopasowywanie wzorów. Jest to z pewnością przydatne w tego rodzaju problemach.
Jonathan M. Davis
W rzeczywistości ??są to dosłowne znaki, =>są separatorami klucz / wartość w Ruby Hashes i ->uruchamiają lambda :-) ( { ?? => ->{...} }to hash z kluczem "?"i lambda jako wartością.) Ale tak, sposób, w jaki jest używany razem wygląda jak dopasowanie wzoru do pojedynczych znaków :-)
Arnaud Le Blanc
2

Funkcja C - 178 niezbędnych znaków

Skompilowany z GCC, nie generuje żadnych ostrzeżeń.

#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

Pierwszy i ostatni wiersz nie są uwzględniane w liczbie znaków. Są one dostarczane wyłącznie dla wygody.

Wysadzony:

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));
}
Joey Adams
źródło
2

JavaScript - 259 znaków

Moja implementacja jest bardzo rekurencyjna, więc stos zostanie przepełniony, jeśli zostanie użyty wyjątkowo długi wzorzec. Ignorując znak plus (który mogłem zoptymalizować, ale nie dla uproszczenia), dla każdego tokena stosowany jest jeden poziom rekurencji.

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

Funkcja czasami zwraca liczbę zamiast wartości logicznej. Jeśli to jest problem, możesz użyć go jako !!glob(pattern, str).


Niegolfowany (raczej nieupoważniony), który ma służyć jako użyteczny zasób:

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

Zauważ, że indeksowanie do znaków ciągu znaków jak dla elementów tablicy nie jest częścią starszego standardu językowego (ECMAScript 3), więc może nie działać w starszych przeglądarkach.

Proszę wstać
źródło
1

Python (454 znaki)

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
Hoa Long Tam
źródło
1

D: 363 znaków

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

Bardziej czytelnie:

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);
}
Jonathan M. Davis
źródło
1

golfscript

{{;;}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;

jest zbudowany z funkcji, które zużywają dwa argumenty ze stosu, sip, i generują pojedynczą wartość logiczną. jest trochę cholerstwa, aby dostosować to do leniwych i leniwych lub operatorów. Naprawdę wątpię, aby takie podejście było prawie optymalne lub nawet we właściwym kierunku.

jest też kilka zabawnie głupich momentów, takich jak wyskakiwanie '*'ze wzoru, pochłanianie '*'porównania, tylko po to, aby zdać sobie sprawę, że kolejna gałąź nie pasuje. aby zejść w dół drugiej gałęzi, potrzebujemy wzoru z '*'przodu, ale zużyliśmy ten oryginalny wzór, kiedy go otworzyliśmy '*', i zużyliśmy '*', więc aby ponownie uzyskać wzór, ładujemy nowy błyszczący łańcuch stały '*'i dodaj go na miejsce. robi się jeszcze bardziej brzydszy, ponieważ z jakiegoś powodu dopasowywanie znaków musi odbywać się za pomocą wartości ascii, ale powrót do łańcucha wymaga ciągów.

mniej golfa

{[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;

testy

{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
roobs
źródło
1

C # (251 znaków)

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

Nieco bardziej czytelny:

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

Wiem, wiem ... z wyjątkiem globusów zawierających odwrotny ukośnik. Co jest naprawdę niefortunne. W przeciwnym razie byłoby naprawdę sprytnie. :(

Timwi
źródło