Реалізація глотчерів


15

Реалізуйте функцію узгодженого шаблону та рядка, поверніть true, якщо шаблон відповідає рядку WHOLE, інакше false.

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

  • ? відповідає будь-якому одному символу
  • + відповідає одному або більше символів
  • * відповідає нулю або більше символів
  • \ втечі

Правила:

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

Приклади:

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

Деякі приклади втечі, будь ласка? ("\")
Енадцять

Відповіді:


4

Гольфскрипт - 82 символи

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

Припускає, що в рядках немає нових рядків. Повертає порожній масив для false, а не порожній масив для true (відповідає визначенню golfscript true / false).

Це нерекурсивне рішення (за винятком послідовних *s), яке підтримує список позицій у рядку шаблону iтаким, що pattern[0..i]відповідає string[0..cur].

Це має потенціал працювати дуже довго. Ви можете додати .&після, :C%щоб запобігти цьому.


5

Haskell, 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) вилучив непотрібне застереження %, яким користувався&

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) речі типу. Визначення цього оператора коштує мені декількох символів без користі, але приємно мати ключове слово для голови.

Мені хотілося б, щоб пізніше тріщина була гольфскриптом, якщо дозволяє час.

редагувати: видалено непотрібну третю гілку у випадку "*" після прочитання рубінової відповіді 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 аргументів. тоді м може бути лямбда, як і її друзі н і глобус. 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('+', '')

Натхненний відповіддю роботів


Я нічого не знаю про рубін, але з вашого коду я дізнався, що доступ до індексів за межі кордону повертає нуль. таким чином, вискакуючи порожню рядок, виходить нульове значення, яке може використовуватися як символ термінального рядка. С-стиль! вишуканий! я думаю, це може бути імітовано в python, передаючи кожну вхідну рядок черезlambda s : list(s)+[None]
roobs

З огляду на це, Рубі вибудував відповідність шаблонів. Це, безумовно, корисно для подібної проблеми.
Джонатан М Девіс,

Насправді ??це буквальні символи, =>це роздільники ключів / значень у 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).


Безкорисливий (немініфікований, скоріше) служить корисним ресурсом:

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

Д: 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, і створюють єдине булеве повернене значення. є трохи міркування навколо, щоб зробити це сумісним з ледачим і ледачим або операторами. я дійсно сумніваюся, що такий підхід знаходиться десь поблизу від оптимального чи навіть у правильному напрямку.

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

Я знаю, я знаю ... за винятком кульок, що містять звороту косу рису. Що насправді прикро. Інакше було б по-справжньому розумно. :(

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.