Implementieren Sie glob Matcher

15

Implementieren Sie eine Funktion aus Muster und Zeichenfolge, die abgeglichen werden soll, und geben Sie true zurück, wenn das Muster mit der GANZEN Zeichenfolge übereinstimmt, andernfalls false.

Unsere Glob-Pattern-Syntax lautet:

  • ? Stimmt mit einem beliebigen Zeichen überein
  • + Stimmt mit einem oder mehreren Zeichen überein
  • * Stimmt mit keinem oder mehreren Zeichen überein
  • \ entkommt

Regeln:

  • Keine Auswertung, kein Konvertieren in regulären Ausdruck, kein Aufrufen einer System-Glob-Funktion.
  • E / A sind nicht erforderlich: Sie können einfach eine Funktion schreiben
  • Kürzeste Siege

Beispiele:

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

Hier ist ein Tipp für den Einstieg: http://en.wikipedia.org/wiki/Backtracking

Ming-Tang
quelle
1
Darf ich ein zusätzliches Tag "pattern-matching" vorschlagen?
dmckee
1
Können Sie klarstellen, was Sie unter "keine Standardfunktion" verstehen? Dass Sie keine Funktionen aus der Standardbibliothek aufrufen können? Wie soll das gehen?
5.
Einige Beispiele zur Flucht bitte? ("")
Eelvex

Antworten:

4

Golfscript - 82 Zeichen

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

Angenommen, die Zeichenfolgen enthalten keine Zeilenumbrüche. Gibt ein leeres Array für false und ein nicht leeres Array für true zurück (in Übereinstimmung mit der Golfscript-Definition von true / false).

Dies ist eine nicht-rekursive Lösung (außer für aufeinanderfolgende *s), bei der eine Liste der Positionen in der Musterzeichenfolge iso geführt wird, dass sie pattern[0..i]übereinstimmen string[0..cur].

Dies kann sehr lange dauern. Sie können hinzufügen , .&nachdem :C%dies zu verhindern.

Nabb
quelle
5

Haskell, 141 Zeichen

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

Funktioniert für alle Eingaben, sowohl für Muster als auch für Zeichenfolgen. Behandelt den nachgestellten Backslash im Muster als wörtliche Übereinstimmung (Verhalten wurde nicht angegeben.)

Dies kann mit dem folgenden Testtreiber ausgeführt werden:

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

Update: Ich habe einen Blog-Beitrag über diese spezielle Antwort geschrieben, da ich denke, dass dies gut zeigt, wie einfach Haskell das Problem codiert.


  • Edit: (181 -> 174) ersetzt dund mmit Operatoren
  • Bearbeiten: (174 -> 151) Inline rinc
  • Bearbeiten: (151 -> 149) entfernt eine unnötig generierte Option in dem +Fall
  • Edit: (149 -> 141) hat eine unnötige Klausel entfernt %, die von behandelt wurde&
MtnViewMark
quelle
2

PHP - 275 243 Zeichen

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

Ungolfed:

<?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
quelle
2

Übermäßig ausführliches Python ( 384 367 Zeichen)

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)

Es ist nicht die kürzeste, aber es ist schön und funktional. Das Dispatch-Dict-Ding in der Mitte könnte vermutlich als Disjunktion über Typendinge umgeschrieben werden (h(p) == '?') and (? lambda body). Das Definieren dieses h-Operators kostet mich einige Zeichen, aber es ist schön, ein Schlüsselwort für head zu haben.

Wenn es die Zeit erlaubt, würde ich es gerne später mal ausprobieren.

edit: unnötige dritte Verzweigung in '*' entfernt, nachdem die Ruby-Antwort von user300 gelesen wurde

roobs
quelle
2

Kürzere Snappier Python 2.6 (272 Zeichen)

Golf gespielt:

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

ungolfed:

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

mit:

  • faul bewertete logische Verwirrung!
  • C-Saiten!
  • süße mehrfache Vergleichssprache!
  • reichlich hässlich!

Wir danken der Antwort von user300 für die Veranschaulichung der Vereinfachung, wenn Sie einen Abschlusswert erhalten, wenn Sie den Kopf aus einer leeren Zeichenfolge entfernen.

Ich wünschte, das Auspacken von Kopf und Schwanz könnte inline während der Erklärung der Argumente von m erfolgen. dann könnte m ein lambda sein, genau wie seine freunde n und glob. python2 kann das nicht, und nach einigem Lesen sieht es so aus, als könnte python3 das auch nicht. Weh.

testen:

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

Ungolfed:

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

Tests:

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

Inspiriert von der Antwort von Roobs

Arnaud Le Blanc
quelle
Ich weiß nichts über Ruby, aber von Ihrem Code habe ich gelernt, dass der Zugriff außerhalb der Grenzen Indizes Null zurückgibt. Wenn Sie also eine leere Zeichenfolge einfügen, erhalten Sie einen Nullwert, der als Zeichenfolgenabschlusssymbol verwendet werden kann. C-Style! raffiniert! Ich schätze, es könnte in Python nachgeahmt werden, indem man jede Eingabezeichenfolge durchlambda s : list(s)+[None]
roobs
Wie es aussieht, hat Ruby Pattern Matching eingebaut. Das ist sicherlich praktisch für diese Art von Problem.
Jonathan M Davis
Eigentlich ??sind das wörtliche Zeichen, =>sind Schlüssel / Wert-Trennzeichen in Ruby Hashes und ->starten ein Lambda :-) ( { ?? => ->{...} }ist ein Hash mit Schlüssel "?"und einem Lambda als Wert.) Aber ja, die Art und Weise, wie es zusammen verwendet wird, sieht aus wie Musterübereinstimmung bei einzelnen Zeichen :-)
Arnaud Le Blanc
2

C-Funktion - 178 notwendige Zeichen

Mit GCC kompiliert, erzeugt dies keine Warnungen.

#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

Die erste und letzte Zeile sind nicht in der Zeichenanzahl enthalten. Sie werden nur zur Vereinfachung bereitgestellt.

In die Luft gesprengt:

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

JavaScript - 259 Zeichen

Meine Implementierung ist sehr rekursiv, sodass der Stapel überläuft, wenn ein extrem langes Muster verwendet wird. Ohne das Pluszeichen (das ich hätte optimieren können, aber der Einfachheit halber nicht verwenden können), wird für jedes Token eine Rekursionsebene verwendet.

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

Die Funktion gibt manchmal eine Zahl anstelle eines Booleschen Werts zurück. Wenn das ein Problem ist, können Sie es als verwenden !!glob(pattern, str).


Ungolfed (eher unbegrenzt) als nützliche Ressource:

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

Beachten Sie, dass die Indizierung in Zeichen einer Zeichenfolge wie bei Array-Elementen nicht zum älteren Sprachstandard (ECMAScript 3) gehört und daher möglicherweise in älteren Browsern nicht funktioniert.

PleaseStand
quelle
1

Python (454 Zeichen)

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

D: 363 Zeichen

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

Mehr leserlich:

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

Es besteht aus Funktionen, die zwei Argumente aus dem Stapel s und p verwenden und einen einzigen booleschen Rückgabewert erzeugen. Es gibt ein bisschen Mist, um das mit den faulen und und faulen oder Operatoren kompatibel zu machen. Ich bezweifle wirklich, dass dieser Ansatz bei weitem nicht optimal oder sogar in die richtige Richtung ist.

Es gibt auch ein paar unterhaltsame, dumme Momente, z. B. das Auftauchen eines '*'Musters, das Aufnehmen des '*'Vergleichs, um dann zu erkennen, dass der nachfolgende Zweig nicht passt. Um den anderen Zweig hinunterzugehen, benötigen wir das Muster mit dem '*'auf der Vorderseite, aber wir haben dieses ursprüngliche Muster verbraucht, als wir das geöffnet haben '*', und wir haben das verbraucht. '*'Um das Muster wieder zu erhalten, laden wir eine glänzende neue Zeichenfolge konstant '*'und voranstellen. Es wird sogar noch hässlicher, weil aus irgendeinem Grund die Zeichenübereinstimmung mit ASCII-Werten erfolgen muss, aber das Zurückstellen auf die Zeichenfolge Zeichenfolgen erfordert.

weniger golf golfscript

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

Tests

{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
quelle
1

C # (251 Zeichen)

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

Etwas besser lesbar:

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

Ich weiß, ich weiß ... mit Ausnahme von Globs, die den Backslash enthalten. Welches ist wirklich unglücklich. Sonst wäre es wirklich klug gewesen. :(

Timwi
quelle