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


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.


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

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

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


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' 

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


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

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.


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

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

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

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. :(

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.