메타 정규식 골프


29

이 xkcd 의 정신으로

여기에 링크 설명을 입력하십시오

임의의 쌍의 목록으로 정규식 골프를하는 프로그램을 작성하십시오. 프로그램은 최소한 정규 표현식을 짧게 만들려고 시도해야합니다. 출력 /^(item1|item2|item3|item4)$/또는 유사한 프로그램 은 허용되지 않습니다.

스코어링은 가장 짧은 정규식을 생성하는 기능을 기반으로합니다. 테스트 목록, 성공 및 실패 미국 대통령 후보자들입니다 발견 여기에 (감사 @ 피터). 물론이 프로그램은 모든 분리 된 목록에 대해 작동해야하므로 대통령에 대한 답변을 반환하는 것은 중요하지 않습니다.


3
/^item1|atem2|item3|item4$/의도하지 않은 우선 순위를 가질 수 있습니다 (문자열은로 시작 item1, 포함 atem2, 포함 item3또는로 끝나야 함 item4).
Konrad Borowski

7
생성 된 정규 표현식의 크기를 기본으로 한 스코어링 시스템을 보유한 경우 이는 더욱 흥미로운 과제입니다.
피터 테일러

1
XKCD 타이틀 텍스트의 정신에서 성공하고 실패한 미국 대통령 후보 . (NB 나는 Wikipedia 다음에 손 으로이 목록을 만들었 으므로 작은 오류가있을 수 있습니다. 승자와 일치하는 모든 성은 목록을 구별하는 것이 불가능하기 때문에 패자 목록에서 제거했지만 의도적으로 중복 제거하지는 않았습니다) .
피터 테일러

4
랜달 먼로 (Randall Munroe)가 우리보다 코드 골프 문제를 더 잘 쓴 사람인지 궁금합니다.
Johannes Kuhn

6
랜달 먼로가이 질문을 던질 지 궁금합니다.
boothby

답변:


8

펄 (111 110 122 자)

use Regexp::Assemble;@ARGV=shift;my$r=new Regexp::Assemble;chomp,add$r "^\Q$_\E\$"while<>;$_=as_string$r;s/\(\?:/(/g;print

Regexp::Assemble정규식을 최적화하기 위해 호출 된 CPAN 모듈을 사용 합니다. 정규 표현식에 대해 더 나은 언어는 무엇입니까 Perl.

또한 재미를 위해 읽을 수있는 버전 (의 도움으로 제작 -MO=Deparse).

use Regexp::Assemble;
my $r = Regexp::Assemble->new;
while (<>) {
    chomp($_);
    $r->add("^\Q$_\E\$");
}
$_ = $r->as_string;
# Replace wasteful (?:, even if it's technically correct.
s/\(\?:/(/g;
print $_;

샘플 출력 (이후 CTRL-D를 수행했습니다 item4).

$ perl assemble.pl
item1
atem2
item3
item4
^(item[134]|atem2)$

또한 보너스로 질문의 모든 단어에 대한 정규식을 작성하고 있습니다.

^(a((ttemp)?t|llowed\.|rbitrary)?|\/\^item1\|atem2\|item3\|item4\$\/|s(ho(rt,|uld)|imilar)|p((air|lay)s|rogram)|(Writ|mak|Th)e|l(ists\.|east)|o([fr]|utputs)|t(h(at|e)|o)|(jus|no)t|regex|golf|with|is)$

또한 사장 목록 (262 바이트).

^(((J(effer|ack|ohn)s|W(ashingt|ils)|Nix)o|Van Bure|Lincol)n|C(l(eveland|inton)|oolidge|arter)|H(a(r(rison|ding)|yes)|oover)|M(cKinley|adison|onroe)|T(a(ylor|ft)|ruman)|R(oosevelt|eagan)|G(arfield|rant)|Bu(chanan|sh)|P(ierce|olk)|Eisenhower|Kennedy|Adams|Obama)$

이것은 하나의 목록에 대해 stdin을 읽고 다른 목록을 강제로 비 웁니다. 확실히 그것은 질문이 요구하는 것이 아닙니다?
피터 테일러

1
@ PeterTaylor : 글쎄, 두 번째 목록이 중요하지 않다. 두 번째 목록에 첫 번째 목록이 중복되지 않으면 정규 표현식이 유효합니다. 더 짧은 정규 표현식을 사용하는 것이 좋지만, 나는 게으르다.
Konrad Borowski

IMO는 최소한 폐기하더라도 입력으로 받아 들일 수있는 방법이 있어야합니다.
피터 테일러

@ 피터 테일러 : 당신이 그렇게 말한다면. 내 프로그램은 이제 두 가지 인수를 취하는데 그 중 하나는 첫 번째 목록입니다.
Konrad Borowski

4
이것은 멋지다; 그러나 가능한 모든 전체 단어를 일치시켜 (다른 목록의 경우) 제외를 만들기 때문에 불필요하게 긴 표현식을 생성합니다 . 원래 골프와 같은 정신이 아닙니다.
Nicole

4

아니 내 솔루션 (! 분명 내가 피터 노르 빅 아니에요)하지만 그를 여기의 (약간 수정) 질문 의례의 솔루션입니다 : http://nbviewer.ipython.org/url/norvig.com/ipython/xkcd1313.ipynb

그가 제공하는 프로그램은 다음과 같습니다 (그의 일이 아니라 내 일).

def findregex(winners, losers):
    "Find a regex that matches all winners but no losers (sets of strings)."
    # Make a pool of candidate components, then pick from them to cover winners.
    # On each iteration, add the best component to 'cover'; finally disjoin them together.
    pool = candidate_components(winners, losers)
    cover = []
    while winners:
        best = max(pool, key=lambda c: 3*len(matches(c, winners)) - len(c))
        cover.append(best)
        pool.remove(best)
        winners = winners - matches(best, winners)
    return '|'.join(cover)

def candidate_components(winners, losers):
    "Return components, c, that match at least one winner, w, but no loser."
    parts = set(mappend(dotify, mappend(subparts, winners)))
    wholes = {'^'+winner+'$' for winner in winners}
    return wholes | {p for p in parts if not matches(p, losers)}

def mappend(function, *sequences):
    """Map the function over the arguments.  Each result should be a sequence. 
    Append all the results together into one big list."""
    results = map(function, *sequences)
    return [item for result in results for item in result]

def subparts(word):
    "Return a set of subparts of word, consecutive characters up to length 4, plus the whole word."
    return set(word[i:i+n] for i in range(len(word)) for n in (1, 2, 3, 4)) 

def dotify(part):
    "Return all ways to replace a subset of chars in part with '.'."
    if part == '':
        return {''}  
    else:
        return {c+rest for rest in dotify(part[1:]) for c in ('.', part[0]) }

def matches(regex, strings):
    "Return a set of all the strings that are matched by regex."
    return {s for s in strings if re.search(regex, s)}

answer = findregex(winners, losers)
answer
# 'a.a|i..n|j|li|a.t|a..i|bu|oo|n.e|ay.|tr|rc|po|ls|oe|e.a'

승자와 패자가 각각 승자와 패자 목록 인 경우 (또는 코스 목록 2 개) 자세한 설명은 기사를 참조하십시오.


8
링크 된 기사가 흥미롭고 읽은 것을 즐겼지만 제기 된 질문에 대답하지 않기 때문에 답변 대신 질문에 대한 의견으로 게시하는 것이 좋습니다.
Gareth

1
당신이 옳습니다, 그것은 의견으로 더 좋았을 수도 있습니다. 나는 그것이 질문에 완벽하게 대답하기 때문에 단순히 답변으로 게시했습니다. 나는 불명료하고 다른 사람의 일을 위해 신용을 얻으려고 노력했을뿐만 아니라 2 쌍의 목록으로 정규식 골프를하는 프로그램을 제공하고 피트니스 기능과 자세한 코드를 제공하는 솔루션을 복사하지 않았습니다. 내가 고려하지 않은 세트 커버 문제 와 유사한 설명과 함께 . 여전히 관련이 없다고 생각되면 알려주세요. 댓글로 삭제하고 게시하겠습니다.
Mike HR

1
다른 사람의 저작물에 대한 크레딧을 얻는 것에 대해 걱정이되는 경우 플래그를 지정하고 "커뮤니티 위키"에 대한 답변을 요청할 수있는 모드를 요청하십시오.
피터 테일러

1
@PeterTaylor 쿨, 나는 그것이 프로토콜인지 몰랐습니다.
Mike HR

2

Factor로 작성된 내 솔루션 :

USING:
    formatting fry
    grouping
    kernel
    math math.combinatorics math.ranges
    pcre
    sequences sets ;
IN: xkcd1313

: name-set ( str -- set )
    "\\s" split members ;

: winners ( -- set )
    "washington adams jefferson jefferson madison madison monroe
monroe adams jackson jackson vanburen harrison polk taylor pierce buchanan
lincoln lincoln grant grant hayes garfield cleveland harrison cleveland     mckinley
 mckinley roosevelt taft wilson wilson harding coolidge hoover roosevelt
roosevelt roosevelt roosevelt truman eisenhower eisenhower kennedy johnson     nixon
nixon carter reagan reagan bush clinton clinton bush bush obama obama" name-set ;

: losers ( -- set )
    "clinton jefferson adams pinckney pinckney clinton king adams
jackson adams clay vanburen vanburen clay cass scott fremont breckinridge
mcclellan seymour greeley tilden hancock blaine cleveland harrison bryan bryan
parker bryan roosevelt hughes cox davis smith hoover landon wilkie dewey dewey
stevenson stevenson nixon goldwater humphrey mcgovern ford carter mondale
dukakis bush dole gore kerry mccain romney" name-set winners diff
    { "fremont" } diff "fillmore" suffix ;

: matches ( seq regex -- seq' )
    '[ _ findall empty? not ] filter ;

: mconcat ( seq quot -- set )
    map concat members ; inline

: dotify ( str -- seq )
    { t f } over length selections [ [ CHAR: . rot ? ] "" 2map-as ] with map ;

: subparts ( str -- seq )
    1 4 [a,b] [ clump ] with mconcat ;

: candidate-components ( winners losers -- seq )
    [
        [ [ "^%s$" sprintf ] map ]
        [ [ subparts ] mconcat [ dotify ] mconcat ] bi append
    ] dip swap [ matches empty? ] with filter ;

: find-cover ( winners candidates -- cover )
    swap [ drop { } ] [
        2dup '[ _ over matches length 3 * swap length - ] supremum-by [
            [ dupd matches diff ] [ rot remove ] bi find-cover
        ] keep prefix
    ] if-empty ;

: find-regex ( winners losers -- regex )
    dupd candidate-components find-cover "|" join ;

: verify ( winners losers regex -- ? )
    swap over [
        dupd matches diff "Error: should match but did not: %s\n"
    ] [
        matches "Error: should not match but did: %s\n"
    ] 2bi* [
        dupd '[ ", " join _ printf ] unless-empty empty?
    ] 2bi@ and ;

: print-stats ( legend winners regex -- )
    dup length rot "|" join length over /
    "separating %s: '%s' (%d chars %.1f ratio)\n" printf ;

: (find-both) ( winners losers legend -- )
    -rot 2dup find-regex [ verify t assert= ] 3keep nip print-stats ;

: find-both ( winners losers -- )
    [ "1 from 2" (find-both) ] [ swap "2 from 1" (find-both) ] 2bi ;



IN: scratchpad winners losers find-both 
separating 1 from 2: 'a.a|a..i|j|li|a.t|i..n|bu|oo|ay.|n.e|ma|oe|po|rc|ls|l.v' (55 chars 4.8 ratio)
separating 2 from 1: 'go|e..y|br|cc|hu|do|k.e|.mo|o.d|s..t|ss|ti|oc|bl|pa|ox|av|st|du|om|cla|k..g' (75 chars 3.3 ratio)

Norvig와 동일한 알고리즘입니다. 가독성이 떨어지는 것이 목표라면 더 많은 캐릭터를 골라 낼 수 있습니다.


1
참고로, 당신은 공식 목록 에서 많은 패자를 잃었습니다 (Burr, Jay, Badnarik, 아마 내가 보지 못하는 다른 사람들). 따라서 결과가 잘못되었습니다. 예를 들어, 첫 번째 정규 표현식은 Burr 및 Jay와 일치하므로 작동하지 않습니다.
elixenide

1

내 코드는 매우 골치스럽고 압축 된 형식은 아니지만 https://github.com/amitayd/regexp-golf-coffeescript/ (또는 특히 src / regexpGolf.coffee의 알고리즘)에서 확인할 수 있습니다 .

Peter Norvig의 알고리즘을 기반으로하며 두 가지 개선 사항이 있습니다.

  1. 문자 세트와 함께 사용할 파트를 작성하십시오 (예 : 유효한 파트가 az, bz 및 cz 인 경우 [ab] z, [ac] z 및 [bc] z 사용).
  2. 각 반복에서 최상의 후보로 구성된 표지 만이 아니라 표지의 "최상의 최적 경로"를 구성 할 수 있습니다.

(또한 선택적인 임의성에 던졌습니다)

이 퀴즈의 승자 / 패자 세트에 대해 76자를 사용하는 정규 표현식을 찾았습니다.

[Jisn]e..|[dcih]..o|[AaG].a|[sro].i|T[ar]|[PHx]o|V|[oy]e|lev|sh$|u.e|rte|nle

블로그 게시물에서 솔버를 coffeescript로 이식하는 방법에 대한 자세한 내용이 있습니다.


2
답변에 코드를 포함시켜 주시겠습니까? 그렇지 않으면 링크를 클릭하지 않고 코드를 볼 수 없습니다!
wizzwizz4
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.