Реализовать Glob Matcher

15

Реализуйте функцию шаблона и строки для сопоставления, верните true, если шаблон соответствует всей строке, в противном случае - false.

Наш синтаксис шаблона глобуса:

  • ? соответствует любому персонажу
  • + соответствует одному или нескольким символам
  • * соответствует нулю или более символов
  • \ ускользает

Правила:

  • Нет eval, нет преобразования в регулярные выражения, нет вызова системной функции glob.
  • Ввод / вывод не требуется: вы можете просто написать функцию
  • Кратчайшие победы

Примеры:

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

Мин-Tang
источник
1
Могу ли я предложить дополнительный тег "сопоставление с образцом"?
dmckee --- котенок экс-модератора
1
Не могли бы вы уточнить, что вы подразумеваете под «нет стандартной функции»? Что вы не можете вызывать функции из стандартной библиотеки? Как это должно работать?
sepp2k
Некоторые примеры побега, пожалуйста? ("\")
Eelvex

Ответы:

4

Golfscript - 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%чтобы предотвратить это.

Nabb
источник
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) заменены dи mс операторами
  • Изменить: (174 -> 151) встраивается rвc
  • Изменить: (151 -> 149) удалил излишне сгенерированный вариант в +случае
  • Изменить: (149 -> 141) удалил ненужное предложение для %, который был обработан&
MtnViewMark
источник
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;}

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));
}
Арно Ле Блан
источник
2

Слишком подробный Python ( 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 стоит мне несколько символов без пользы, но приятно иметь ключевое слово для заголовка.

Я хотел бы иметь треск в игре в гольф позже, если позволит время.

edit: убрал ненужную третью ветку в случае '*' после прочтения ruby-ответа user300

roobs
источник
2

Короче Snappier Python 2.6 (272 символа)

golfed:

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

с участием:

  • лениво оцениваемый логический беспорядок!
  • Струны в стиле C!
  • симпатичная множественная идиома сравнения!
  • много уродливых!

спасибо пользователю user300 за иллюстрацию, как все упрощается, если вы можете получить какое-то значение терминатора при извлечении заголовка из пустой строки.

Я хотел бы, чтобы распаковка головы / хвоста могла быть выполнена inline во время объявления аргументов 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' 
roobs
источник
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}

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

тесты:

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

Вдохновленный ответом Робов

Арно Ле Блан
источник
я ничего не знаю о ruby, но из вашего кода я узнал, что доступ за пределы индексов возвращает ноль. поэтому выталкивание пустой строки приводит к значению nil, которое можно использовать как символ конца строки. C-стиле! изящная! я думаю, что это может быть воспроизведено в python, передав каждую входную строкуlambda s : list(s)+[None]
roobs
Судя по всему, в Ruby встроено сопоставление с образцом. Это, безусловно, удобно для такого рода проблем.
Джонатан М Дэвис
На самом деле они ??являются буквальными символами, =>являются разделителями ключ / значение в Ruby Hashes и ->запускают лямбду :-) ( { ?? => ->{...} }это хеш с ключом "?"и лямбда в качестве значения.) Но да, способ, которым его совместное использование выглядит как сопоставление с образцом на отдельных символах :-)
Арно Ле Блан
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

JavaScript - 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 (скорее unminified), чтобы служить полезным ресурсом:

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) языкового стандарта, поэтому может не работать в старых браузерах.

PleaseStand
источник
1

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

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;

он состоит из функций, которые принимают два аргумента из стека s и p и выдают единственное логическое возвращаемое значение. есть что-то вроде того, чтобы сделать это совместимым с ленивыми и ленивыми или операторами. я действительно сомневаюсь, что этот подход близок к оптимальному или даже в правильном направлении.

Есть также несколько занимательно глупых моментов, таких как выталкивание '*'из шаблона, использование '*'в сравнении только для того, чтобы понять, что последующая ветвь не совпадает. чтобы перейти вниз по другой ветви, нам нужен шаблон с '*'передней частью, но мы использовали этот оригинальный шаблон, когда извлекли его '*', и мы использовали '*', поэтому, чтобы получить шаблон снова, мы загружаем новую блестящую строку постоянный '*', и добавьте его на место. это становится еще страшнее, потому что по какой-то причине сопоставление символов должно быть выполнено со значениями 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
roobs
источник
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; }
}

Я знаю, я знаю ... за исключением глобусов, содержащих обратную косую черту. Что действительно неудачно. В противном случае это было бы действительно умно. :(

Timwi
источник