glob Matcherを実装する


15

一致するパターンと文字列の関数を実装し、パターンがWHOLE文字列に一致する場合はtrueを返し、そうでない場合はfalseを返します。

globパターンの構文は次のとおりです。

  • ? 任意の1文字と一致します
  • + 1つ以上の文字に一致します
  • * 0個以上の文字に一致します
  • \ 逃げる

ルール:

  • 評価なし、正規表現への変換なし、システムglob関数の呼び出しなし。
  • I / Oは不要です。関数を書くだけです
  • 最短勝ち

例:

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

エスケープの例 (「\」)
Eelvex

回答:


4

Golfscript-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)置換されdm、演算子で
  • 編集:(174-> 151)インライン r intoc
  • 編集:(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

過度に詳細な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演算子を定義することは、何の利益ももたらさずにいくつかのキャラクターを犠牲にしますが、headのキーワードを持つことは素晴らしいことです。

時間が許せば、後でゴルフスクリプティングにクラックを入れたいです。

編集:user300のルビーの答えを読んだ後、「*」の場合に不要な3番目のブランチを削除しました


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の引数の宣言中に、ヘッド/テールのアンパックをインラインで実行できることを望みます。その場合、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' 

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

roobsの答えに触発


ルビーについては何も知りませんが、コードから境界外のインデックスにアクセスするとnilが返されることを学びました。そのため、空の文字列をポップすると、nil値が生成され、文字列ターミネータシンボルとして使用できます。Cスタイル!気の利いた!各入力文字列を渡すことにより、Pythonで模倣できると思いますlambda s : list(s)+[None]
-roobs

その外観から、Rubyにはパターンマッチングが組み込まれています。これはこの種の問題には確かに便利です。
ジョナサンMデイビス

実際に??はリテラル文字で=>あり、Rubyハッシュのキー/値セパレーターであり->、ラムダを開始{ ?? => ->{...} }します: "?"-) (キーと値としてのラムダを含むハッシュです。) :
アルノールブラン

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文字

私の実装は非常に再帰的であるため、非常に長いパターンを使用するとスタックがオーバーフローします。プラス記号(私は最適化できたかもしれませんが、簡単にするために選択しませんでした)を無視して、1つの再帰レベルが各トークンに使用されます。

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(むしろ縮小されていない)

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

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

ゴルフスクリプト

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

スタックから2つの引数sとpを消費し、単一のブール値の戻り値を生成する関数で構築されています。lazy and and lazy or演算子と互換性を持たせるために、いじくり回しがあります。このアプローチが最適に近い場所にあるのか、正しい方向にあるのか、私は本当に疑っています。

また'*'、パターンから飛び出し'*'、比較で消費するなど、後続のブランチが一致しないことを認識するために、いくつかの面白い愚かな瞬間があります。他のブランチを下るには'*'、前面にあるパターンが必要ですが、ポップしたときに元のパターンを使用しました'*'を消費し'*'、を消費したため、再びパターンを取得するには、光沢のある新しい文字列をロードします定数'*'、および所定の場所に追加します。何らかの理由でASCII値を使用して文字の照合を行う必要があるため、さらにevenいものになりますが、文字列の前に戻すには文字列が必要です。

少ないゴルフ

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

知っています、知っています...バックスラッシュを含むグロブを除きます。これは本当に残念です。そうでなければ、本当に賢かったでしょう。:(

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.