Meta regex golf


29

Bu xkcd ruhu içinde

enter link description here

Düzenli çiftleri listelerde toplayan regex golf oynayan bir program yazın. Program en azından regex'i kısaltmaya çalışmalı, sadece çıktı /^(item1|item2|item3|item4)$/veya benzeri bir programa izin verilmiyor.

Puanlama en kısa regex üretme yeteneğine dayanmaktadır. Test listeleri, başarılı ve başarısız ABD başkan adayları olanlardır bulundu burada (teşekkürler @Peter). Tabii ki, program bütün ayrık listeler için çalışmalı, bu yüzden başkan için bir cevap döndürmüyor sayılır.


3
/^item1|atem2|item3|item4$/büyük olasılıkla istenmeyen bir önceliğe sahip (dize ile başlamalı item1, içermeli atem2, içermeli item3veya bitmeli item4).
Konrad Borowski

7
Öncelikle üretilen regexlerin boyutuna dayalı bir skorlama sistemine sahip olsaydı, bu daha ilginç bir zorluk olurdu.
Peter Taylor

1
XKCD başlık metninin ruhuna göre, başarılı ve başarısız ABD başkan adayları . (NB Vikipedi'yi takiben bu listeyi el ile yaptım , bu nedenle küçük hatalar olabilir; kaybedenler listesinden bir kazananla eşleşen tüm soyadları kaldırdım, aksi halde listeleri ayırt etmek imkansız, ancak kasıtlı olarak başka bir şekilde tekilleştirmedim) .
Peter Taylor

4
Randall Munroe’nin bizden daha iyi bir kod golf mücadelesi yazarı olup olmadığını merak ediyorum ...
Johannes Kuhn

6
Randall Munroe'nin bu soruyu aşağı atıp atmayacağını merak ediyorum.
08'de

Yanıtlar:


8

Perl (111 110 122 karakter)

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

Bu Regexp::Assemble, normal ifadeleri optimize etmek için çağrılan CPAN modülünü kullanır . Çünkü normal ifadeler için Perl'den daha iyi olan dil hangisidir?

Ayrıca, sadece eğlence için okunabilir versiyonu, (yardımı ile yapılır -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 $_;

Örnek çıktı (sonra CTRL-D yaptım item4).

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

Ayrıca, bonus olarak, sorudaki her kelimenin regex'ini yazıyorum.

^(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)$

Ayrıca, başkanların listesi (262 bayt).

^(((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)$

This appears to read stdin for one list and force the other to be empty. Surely that's not what the question is asking for?
Peter Taylor

1
@PeterTaylor: İkinci listenin herhangi bir önemi yok. İkinci listede ilk listenin kopyası bulunmuyorsa, regexp geçerlidir. Daha kısa bir regexp olması güzel olurdu, ama biraz tembelim.
Konrad Borowski

IMO, en azından atmış olsanız bile, giriş olarak almanın bir yolunu bulmalıdır.
Peter Taylor

@PeterTaylor: If you say so. My program now takes two arguments, one of them being the first list.
Konrad Borowski

4
Bu havalı; o mümkün olan her eşleştirerek (başka liste için) dışlanmayı yaratır çünkü ancak gereksiz yere uzun ifadeleri üretir tam kelime. Asıl golf ile aynı ruh değil.
Nicole

4

Benim çözümüm değil (tabii ki ben Peter Norvig değilim!) Ama işte (biraz değiştirilmiş) soru nezaketinin çözümü: http://nbviewer.ipython.org/url/norvig.com/ipython/xkcd1313.ipynb

Verdiği program aşağıdaki gibidir (eseri benim değil):

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'

Kazananlar ve kaybedenler sırasıyla kazananlar ve kaybedenler listeleri ise (veya elbette herhangi bir 2 listeden) ayrıntılı açıklamalar için makaleye bakın.


8
Bağlantılı makale ilginç olsa ve okumaktan zevk alırken, bu soruyu cevaplamak yerine soru yerine bir cevap olarak yorumlamak daha iyi olurdu.
Gareth

1
You're right, it might have been better as a comment, I posted it as an answer simply because it answers the question perfectly. I didn't copy out the solution as I thought that'd be disingenuous and trying to take credit for someone elses work, in addition to providing a program that plays regex golf with 2 pairs of lists it also gives a fitness function and detailed code explanation along with the parallel to the set cover problem which I hadn't considered. If you still think it's not relevant, let me know, I'll delete and post as a comment.
Mike H-R

1
Başkasının çalışması için kredi almak konusunda endişeleniyorsanız, cevabınızı "Topluluk wiki" yapmak için işaretleyin ve bir mod isteyin.
Peter Taylor

1
@PeterTaylor harika, protokol olduğunu bilmiyordum, yapıldı.
Mike HR,

2

Benim çözüm yazılmış 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 ile aynı algoritma. Okunabilirliği incitmek hedef ise, o zaman muhtemelen birçok karakterden kurtulabilirsiniz.


1
Bilginize, resmi listeden kaybedenler birkaçı eksik (Burr, Jay, Badnarik, muhtemelen göremiyorum diğerleri). Yani sonuçlarınız yanlış; örneğin, ilk regex çalışmıyor, çünkü Burr ve Jay ile eşleşiyor.
elixenide

1

Kodum çok golf sahası ve yoğunlaşmış bir şekilde değil, ancak kontrol edebilirsiniz. https://github.com/amitayd/regexp-golf-coffeescript/ (or specifically the algorithm at src/regexpGolf.coffee).

It's based on Peter Norvig's algorithm, with two improvements:

  1. Create parts to use with character sets (i.e. use [ab]z, [ac]z, and [bc]z if valid parts are az, bz and cz).
  2. Her yinelemede sadece en iyi adaydan yapılmış bir kapak değil, kapakların "en iyi yollarını" oluşturmaya izin verin.

(Ve ayrıca isteğe bağlı bir rastgele fırlattı)

Bu quizde kazanan / kaybedenler için kullandığımda 76 karakter regex buldum:

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

Blog yazımda, çözücüyü coffeescript'e taşıma ile ilgili bazı detaylar .


2
Could you please contain your code in your answer? Otherwise, we can't see the code without clicking on the link!
wizzwizz4
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.