Чи регулярні вирази є мовою програмування?


27

Чи в класичному розумінні регулярні вирази кваліфікуються як мова програмування?

Мотивація моєї цікавості - це питання, яке я просто переглянув, і запитав "чи можна регулярно виразити X"? і це змусило мене замислитися, що можна сказати в загальному сенсі про можливі рішення, використовуючи їх.

Я в основному запитую, "чи регулярні вирази Turing завершені"?


9
В основному, ви запитуєте "чи регулярні вирази Turing завершені"?
FrustratedWithFormsDesigner

Було б здорово, якби хтось детальніше розробив, але так
Аарон Анодід

4
"Регулярні вирази turing повні" вимагає розуміння типів мови та chomsky hierchary

5
(На 1 хвилину пізніше від редагування), і якщо ви хочете пройти по цьому шляху запитань і пояснень, ви можете заглянути в обмін теорією cs . Насосна леммой є найпростішими спростування для «може відповідати регулярному мови а ^ пь ^ п» (який Matchable машини Тьюринга).

1
Я думаю, він запитує, чи зможе він розмістити його у своєму резюме у розділі "Мови програмування". Відповідь у цьому випадку - ні. Це стосується розділу "Технології".
Ніл

Відповіді:


46

Регулярні вирази - це особливий вид формальної граматики, що використовується для розбору рядків та іншої текстової інформації, яка в теорії формальної мови відома як "Регулярні мови". Вони не є мовою програмування як такою. Вони є скоріше скороченням для кодування, яке в іншому випадку було б вкрай стомлюючим для впровадження та ще більш заплутаним, ніж інколи Regex, що виглядає дурно.

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

Якщо ви хочете мову, схожу на Regex, спробуйте J.


1
+1, я подивився, але не міг знайти хорошого обговорення / протиріччя Тюрінгу повноти регулярних виразів.
FrustratedWithFormsDesigner

1
@ davidk01 - Стільникові автомати можуть бути завершеними (хоча хороших компіляторів важко знайти), регулярних виразів немає. Ви можете робити нетривіальні обчислення, так, але є досить тривіальні речі, які ви також не можете зробити. Тюрінг повних стільникових автоматів може розглядатися як мова програмування, оскільки в принципі ви можете написати з ними будь-яку програму, яку ви могли б використовувати будь-якою іншою мовою.
пн

1
Також важливо зазначити, що регулярний вираз, який виконує тестування первинності ( montreal.pm.org/tech/neil_kandalgaonkar.shtml#primality_regex ), використовує функції регексів perl, які є більш потужними, ніж "Регулярні вирази" в академічному розумінні, а саме - збережені групи . Звичайні мови не потребують довільної пам'яті.
Ерік В.

5
@WorldEngineer: Є цікаві та корисні мови програмування, які не закінчують Тьюрінга. Datalog, SQL та ACL2 - це декілька прикладів, які приходять до уваги, а також будь-яка кількість сильно нормалізуючих лямбда-обчислень, що використовуються в таких речах, як докази теореми на основі типу.
Райан Калпеппер

1
Не всі мови програмування закінчуються. Наприклад, чисто контекстні декларативні мови на зразок XML, які не завершуються без парного перекладача, можуть вважатися мовами програмування. Все залежить від вашого визначення поняття "мова програмування". Все, що вам потрібно, щоб перетворити "звичайну" мову на "безконтекстну" мову - це стек, що висувається. Тоді це черепахи аж донизу.
Еван Плейс

14

Важко відповісти на запитання на кшталт «є X Y », якщо учасники використовують дебати різних визначень X і Y . Можливо, для деяких визначень відповідь - «так», а для деяких визначень - «ні». Особливо, якщо відповідь залежить від технічних деталей, де різні визначення відрізняються. Також ця дискусія містить деяку дезінформацію, тому, будь ласка, потерпіть довшу відповідь.

Що ми маємо на увазі під « мовою програмування »?

Проста відповідь може бути "мовою, що використовується для створення програм". Звичайно, але: які програми? Що з мовою, яку можна використовувати для створення деяких видів програм, але не інших програм? Ось два конкретні приклади для ілюстрації крайніх випадків:

1) Уявна мова під назвою M працює так: Якщо програма містить одну букву "m", вона створює гру Міночистача. Все інше - синтаксична помилка.

Інтуїтивно це не те, що ми маємо на увазі під «мовою програмування». Але відділ маркетингу M може стверджувати, що технічно він відповідає визначенню, оскільки може бути використаний для створення програми. Звичайно, компілятор робить для вас критичні частини, але це те, що роблять компілятори, чи не так? Компілятор мови С також перекладає кілька простих слів у десятки інструкцій процесора. Компілятор M просто йде далі і робить вашу роботу ще простішою.

2) Якщо ви встановите оригінальну версію знаменитого Turbo Pascal, ви можете писати багато видів програм. Але ви не можете написати гру, яка працює у веб-браузері, оскільки потрібного API просто немає.

Отже, що саме є тим, що робить Turbo Pascal мовою програмування, а M не має? Простіше кажучи, ви можете зробити більше в Pascal, ніж у M. Але, уявіть, у нас є M.NET, який створює гру Minesweeper, що працює в веб-браузері. Отже, зараз у нас є щось, що може зробити Pascal, а M.NET не може, але ми також можемо зробити що, що M.NET може зробити, а Pascal не може. Чому слід вважати переваги Паскаля важливими, а переваги M.NET - неістотними?

Відповідь полягає в тому, що ви можете писати всі види алгоритмів на Pascal, але ви не можете писати алгоритми в M або M.NET. Звичайно, M компілює вашу команду "m", а C складає вашу команду "strcmp". Але ви можете поставити "strcmp" у більш широкому контексті, наприклад порівняти два файли по черзі, або прочитати тисячі рядків і сортувати їх за алфавітом, або ... ну, мільйони інших речей. І саме ця здатність використовувати задані команди в будь-якому алгоритмі складає суть мови програмування.

Що саме таке алгоритм, і що ще важливіше, що таке "будь-який алгоритм"? У інформатиці ми використовуємо слова Тьюрінга-завершені . Ідея полягає в тому, що існує набір комп'ютерних мов, де кожна з них здатна імітувати їх усіх. Однією з таких мов є машина Тюрінга, і тому їх називають так. Pascal є, C є, Java є, Python є, Lisp є, Smalltalk є, навіть XSLT є. Наш гіпотетичний M і M.NET є НЕ існує. Ви можете дізнатися про це більше в будь-якому університеті, який пропонує гідний курс інформатики, але ідея полягає в тому, що мова, якою володіє Тюрінг, може робити всещо може зробити інша мова Тюрінга, якщо ви надаєте їм мінімально необхідний API. (Якщо ви надаєте Pascal якийсь API для веб-браузера, ви можете створювати всі види ігор у веб-браузері. Якщо ви надаєте API веб-браузера M, ви все ще можете створити Мережу.) Ми можемо метафорично сказати, що якщо ви видалите всі API з мови програмування, важливим є те, що залишається.

Що ми маємо на увазі під " регулярними виразами "?

Різні мови програмування реалізують їх дещо по-різному. Але первісна ідея полягала в тому, що регулярні вирази виражають так звані регулярні мови . Зауважте, що ми говоримо не тут про мови програмування, а про (псевдо-) людські мови. Уявіть, що ви знайдете якесь екзотичне плем'я, яке розмовляє мовою, що складається лише зі слів «ба», «баба», «бабаба» тощо. Ви можете описати цю мову словесно як "склад" ba ", повторений один або кілька разів" або використовуючи регулярний вираз як "(ba) +".

Регулярні вирази повинні виражати: "нічого", "ця буква", "це, за цим", "те чи інше", "це, повторене один чи кілька разів", і "не це". - Це математичне визначення. Все інше - це лише зручний ярлик, побудований з попередніх компонентів. Наприклад, "це, повторне два-три рази" можна перекласти як "це, за ним слідує, за цим (це чи нічого)", але було б зручніше написати "ба {2,3}", ніж "баба (ба)? ".

У реальному житті типова реалізація "регулярних виразів" реалізує більше, ніж це. Наприклад, використовуючи математичне визначення, мова "аба", "аабаа", "ааабаа" і так далі - будь-яке число "а", за яким слідує "б", а за ним те саме число "а" "s - не є звичайною мовою. Однак багато "регулярних виразів", що використовуються сьогодні, могли його виявити, використовуючи додаткове поняття "те саме, що ми знайшли раніше", написане як "(a +) b \ 1". Використовуючи цю додаткову концепцію, ми можемо зробити кілька цікавих речей, наприклад виявити слова, що складаються з простої кількості літер. Проте ми не можемо виконати жодного алгоритму ... для пояснення чому,

Отже, повертаємось до початкової теми: чи регулярні вирази (визначені як: вирази, що описують регулярні мови в ієрархії Хомського, або як: колишня, плюс операція \ 1), мова програмування (визначена як: Turing-завершена)? Відповідь - ні . Ні, ви не можете реалізувати жоден алгоритм, використовуючи регулярні вирази, а можливість реалізації будь-якого алгоритму - це те, що люди, які вивчають інформатику, зазвичай розуміють як суть мови програмування.

Звичайно, кожен може змінити відповідь, наполягаючи на іншому визначенні . Як я писав на початку, тут важливі технічні деталі. Якщо ви їх неправильно отримаєте, ви отримаєте неправильну відповідь.

І якщо вас не цікавлять технічні деталі, відповідь може бути: Чи можете ви використовувати регулярні вирази (і більше нічого) для створення програми? Ні. То чому б це називати мовою програмування? (Однак така відповідь була завантажена і видалена тут, саме тому я написав цю довшу версію.)

EDIT: Також кожен може створити бібліотеку, реалізуючи свій власний новий варіант "регулярних виразів" з деякими додатковими новими можливостями. В якийсь момент нових функцій може бути достатньо для того, щоб вся система набула статусу Тьюрінга. Тривіальним прикладом може бути вбудовування мови Тюрінга, використовуючи новий синтаксис; але це теж може статися менш очевидно. Можливо, це вже сталося.


0

У .Net, Regex не тільки може обробляти декілька форм умовних умов, використовуючи різні комбінації чергування та циклів пошуку, але й може маніпулювати власним стеком.

(?xm)
    (?>
        <(?<Tagname>table)[^>]*>
    )
(?(Tagname)
    (
        </(?(?!\k'Tagname')(?<-Tagname>))*\k'Tagname'>(?<-Tagname>)
    |
        (?>
            <(?<Tagname>[a-z][^\s>]*)[^>]*>
        )
    |
        [^<]+
    )+?
    (?(Tagname)(?!))
)

Наприклад, це невеликий фрагмент, який я написав для отримання таблиці HTML. На відміну від інших двигунів регулярних виразів, це керує стеком колекцій захоплення (push, peek і pop) і може обробляти вкладені об'єкти. У мене є більш складний, але він є власним сортом.

Я думаю, що в цьому прикладі Regex можна розглядати як такий, що має всі основні вимоги до мови програмування. Він має змінні, вбудовану пам'ять, умовні умови, вхід і вихід, він компілюється за допомогою одного з декількох двигунів компіляції регулярних виразів (.Net у цьому випадку).

У відповідь на надмірно використаний трасування до (НІКОЛИ) Синтаксичного розбору HTML з Regex, я пішов уперед і опублікував попередньо набрану відповідь, яку я можу розмістити: Парсинг HTML

Приклад анотера (лише демонстрація):

Function Regex("<(td>)((?:[^<]*(?(?!</\1)<))*)</\1")
    Group(0) = "<"
    Group(1) = "td>"
    Group(0) += Group(1)
    Group(2) = LoopMethod()
    Group(0) += Group(2)
    Group(0) += "</" & Group(1)
    Return Group()
End Function

Function LoopMethod()
    retGroup = ""
    Do
        tmpGroup = Everything that is NOT an Opening HTML Delimeter
        If the Text following tmpGroup Does NOT Equal "</" & Group(1) Then
            tmpGroup += "<"
            retGroup += tmpGroup
        Else
            Exit Do
        End If
    Loop
    Return retGroup
End Function

Знову ж таки, для папуг HTML: Розбір HTML

Це показує простіший регулярний вираз, що виконує цикли та умови (алгоритми?). Єдине, чого не вистачає - це фактичні математичні обчислення. Це більш детальне регулярне вираження, яке просто витягує комірку TD ефективніше, ніж типовий метод ((. *?)).

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


Якщо ви "тестуєте" це, і це не працює, ви повинні усвідомити, що більшість "тестерів" двигуна регулярних вивірок не справляються. Net Regex (балансуючі групи). Вам доведеться фактично використовувати це в програмі .Net.
Суамер

3
О боже, це є першим доказом того, чому ви ніколи не використовуєте регулярні вирази для розбору html . Колись.
Такрой

@Tacroy Приємно бачити, як хтось поспішив, щоб порадити папузі про розбір HTML з допомогою регулярного виразу. Хоча це не для слабких сердець, поєднання реджексів, подібних до вищевказаних, із стеком - це основний (та ефективний) рецепт побудови без контекстного аналізатора.
Еван Плейс

1
У відповідь на «Папуга». Я створив це: Розбір HTML
Суамер

Це не регулярний вираз, якщо він приймає контекстні мови. Це ще якийсь DSL, який є суперсетью Regex. Ім'я продавця цього не змінює
Caleth

0

Хоча одна знахідка / заміна в регулярному виразі не є повною мовою програмування Тьюрінга, як пояснено попередніми відповідями, якщо ви дозволяєте використовувати повторні дії заміни на регулярні вирази, то так, ви можете кодувати будь-яку машину Тьюрінга, використовуючи регулярний вираз:

Повторна знахідка / заміна регулярними виразами є повною мовою програмування Тьюрінга

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

Щоб довести повноту тюрінга, достатньо кодувати машину Тьюрінга при регулярному пошуку / заміні виразів. Припустимо, що стан редактора:

0000#12345:01-5:0#0000000

яку можна прочитати як стрічку символів із читачем на ній:

[left symbols]#[set of states]:[set of symbols]-[current state]:[current symbol]#[right symbols]

Для правила читання 0 у стані 5, записуючи 1 та змінивши його стан на 3 та перемістившись вліво, ми абстрагуємо його, використовуючи наступні позначення:

5:0 => 1, 3:[left]

Ми кодуємо попереднє позначення в регулярний вираз пошуку:

(\d)#(1)(2)(3)(4)(5):(0)(1)-5:0#

та його вираз заміни (схожий на JavaScript)

#12345:01-$4:$1#$8

Гаразд, тепер як закодувати багато правил? Ми використовуємо конкатенацію з orоператором |для пошуку регулярних виразів і поєднуємо результати в заміні, нумеруючи номери груп із зрушеннями. Наприклад, розглянемо безліч чотирьох правил.

5:0 => 1, 3:left
3:0 => 1, 5:right
5:1 => 1, 5:right
3:1 => 1: 3:stop

Ми кодуємо їх у виразі пошуку та заміни:

Search:
(\d)#(1)(2)(3)(4)(5):(0)(1)-5:0#|#(1)(2)(3)(4)(5):(0)(1)-3:0#(\d)|#(1)(2)(3)(4)(5):(0)(1)-5:1#(\d)|#(1)(2)(3)(4)(5):(0)(1)-3:1#

Replace by:
$15$23#12345:01-$4$13$21$27:$1$16$24$31#$8

Спробуйте це у вашому улюбленому механізмі javascript:

function turingstep(s) {
  return s.replace(/(\d)#(1)(2)(3)(4)(5):(0)(1)-5:0#|#(1)(2)(3)(4)(5):(0)(1)-3:0#(\d)|#(1)(2)(3)(4)(5):(0)(1)-5:1#(\d)|#(1)(2)(3)(4)(5):(0)(1)-3:1#/g,"$15$23#12345:01-$4$13$21$27:$1$16$24$31#$8");
}

var tape = "0000#12345:01-5:0#0000000"
for(var i = 0; i < 6; i++) {
  console.log(tape)
  tape = turingstep(tape)
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.