Meta regex golf


29

У дусі цього xkcd

введіть опис посилання тут

Напишіть програму, яка грає в регулярний гольф з довільними парами списків. Програма повинна хоча б намагатися зробити регулярний вираз короткою, програма, яка тільки виводить /^(item1|item2|item3|item4)$/або подібне, заборонена.

Оцінка балів базується на здатності генерувати найкоротший вираз. Списки тестів - це списки успішних та невдалих кандидатів у президенти США, знайдені тут (дякую @Peter). Звичайно, програма повинна працювати для всіх непересічних списків, тому просто повернення відповіді для президента не враховується.


3
/^item1|atem2|item3|item4$/ймовірно, має ненавмисний пріоритет (рядок повинен починатись item1, містити atem2, містити item3або закінчувати item4).
Конрад Боровський

7
Це було б більш цікавим завданням, якби у нього була система балів, заснована в основному на розмірі генерованих регулярних виразів.
Пітер Тейлор

1
У дусі заголовного тексту XKCD успішні та невдалі кандидати в президенти США . (Примітка. Я склав цей список вручну, переходячи до Вікіпедії , тому можуть бути невеликі помилки; я вилучив зі списку переможених всі прізвища, які відповідають переможцю, тому що в іншому випадку розрізнення списків неможливо, але я навмисно не дублююсь інакше) .
Пітер Тейлор

4
Цікаво, чи Рандалл Манро кращий автор викликів кодового гольфу, ніж ми ...
Йоханнес Кун

6
Цікаво, чи не вдасться Рандалл Манро з цього питання.
стенд

Відповіді:


8

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

Для цього використовується модуль CPAN, викликаний Regexp::Assembleдля оптимізації регулярних виразів. Тому що яка краща мова для регулярних виразів, ніж 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: Ну, не те, що другий список має будь-яке значення. Якщо у другому списку немає дублікатів першого списку, регулярне вираження дійсне. Було б непогано мати коротший регепс, але я такий собі ледачий.
Конрад Боровський

ІМО, ви, принаймні, повинні мати спосіб сприйняти це як вхідне, навіть якщо ви потім відкинете його.
Пітер Тейлор

@PeterTaylor: Якщо ви так говорите. Зараз у моїй програмі є два аргументи, один з яких - перший список.
Конрад Боровський

4
Це круто; але він створює непотрібні довгі вирази, оскільки створює виключення (для будь-якого іншого списку) шляхом зіставлення кожного можливого повного слова. Який не зовсім такий дух, як оригінальний гольф.
Ніколь

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
Хоча пов’язана стаття є цікавою, і мені сподобалося її читати, це було б краще розмістити як коментар до питання, а не як відповідь, оскільки воно не відповідає на поставлене питання.
Гарет

1
Ви маєте рацію, це може бути краще як коментар, я розмістив це як відповідь просто тому, що він ідеально відповідає на питання. Я не копіював рішення, бо вважав, що це буде недобросовісно і намагаюся взяти кредит на роботу когось з інших людей, на додаток до надання програми, яка грає в регулярний гольф з двома парами списків, вона також дає фітнес-функцію та детальний код пояснення разом з паралеллю заданої обкладинці, яку я не розглядав. Якщо ви все ще вважаєте, що це не актуально, повідомте мене, я видаляю та публікую коментар.
Майк HR

1
Якщо ви турбуєтесь про те, щоб взяти кредит на чужу роботу, позначте і попросіть мода зробити свою відповідь "Вікі спільноти".
Пітер Тейлор

1
@PeterTaylor круто, я не знав, що це протокол, зроблено.
Майк HR

2

Моє рішення написано на " Фактор" :

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)

Це той самий алгоритм, що і у Норвіга. Якщо завдавати шкоди читабельності - це мета, то, ймовірно, ви можете перемогти ще багато символів.


1
FYI, вам не вистачає купу програшів з офіційного списку (Берр, Джей, Баднарик, напевно, інших, яких я не бачу). Отже, ваші результати неправильні; наприклад, перший регулярний вираз не працює, оскільки він відповідає Берру та Джею.
еліксинід

1

Мій код не надто гольф-і скорочений, але ви можете перевірити його за посиланням https://github.com/amitayd/regexp-golf-coffeescript/ (або конкретно алгоритмом на src / regexpGolf.coffee).

Він заснований на алгоритмі Пітера Норвіга з двома вдосконаленнями:

  1. Створіть частини для використання з наборами символів (тобто використовуйте [ab] z, [ac] z і [bc] z, якщо дійсними частинами є az, bz і cz).
  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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.