Обязательно. Вот простой правой рекурсивной грамматики трех правил:
S -> b T
T -> a S
T -> a
Эти три правила позволяют нам построить синтаксический анализатор для распознавания этих строк:
type Parser = String -> (Bool, String)
s :: Parser
s "" = (False, "")
s (c : cs) = if c == 'b' then t cs else (False, cs)
t :: Parser
t "" = (False, "")
t (c : cs)
| c == 'a' && cs == "" = (True, "")
| c /= 'a' = (False, cs)
| otherwise = s cs
Если вы хотите сделать более общий синтаксический анализ, просто специализируются Bool
, чтобы вместо этого иметь некоторую структуру данных, возможно, сохраненную в файле Maybe
, чтобы указать отказ. Возврат (False, ___)
при неудачном анализе помог бы, если бы у S были и другие правила, например, например. S -> T T
и T -> b b
. Затем, когда мы получаем «b», за которым следует (False, ___), мы перемотаем, чтобы попробовать S -> T T
. Такого рода грамматики можно сделать с небольшим количеством жира и рекурсии локтя.
Три правила, описанные выше, будут успешно соответствовать строкам типа «ба», «баба» и т. Д. Мы также могли бы написать эти строки слева рекурсивно:
S -> T a
T -> S b
T -> b
Что произойдет, если вы пытаетесь писать одни и те же парсер выше? Бесконечный цикл, если вы смотрите на фронт строки. Проблема в том, что функция S сначала вызовет функцию T, а затем T сначала вызовет S, и они будут взаимно рекурсивно считать бесконечность. Компьютер не настолько умен, чтобы знать, что постусловия («за которым следует а», «за которым следует а») делает невозможным дальнейшее решение; он просто спускается в ваши функции и надеется, что вы знаете, что делаете.
Как хорошо сочетается комбинатор с фиксированной запятой? Ну, подумайте об этих правилах как о описании дерева: тогда оценка функции пересекает эту глубину дерева, и это конкретное дерево бесконечно в этом направлении.С другой стороны, обход по ширине может основываться на этих правилах и может поднять результат, который использует наименьшую из этих функций, и это «наименее фиксированная точка» для определенной функции, основанной на этой грамматике. Таким образом, поэтому правый комбинатор с фиксированной запятой (основанный либо на diag
в статье, либо на комбинаторе теории решетки) может прекратиться при описании этих правил, тогда как наивная рекурсия не будет.
Благодарим вас за ответ. Что-то я не понимаю в отношении вашего утверждения о выходе оператора fixpoint: хотя я понимаю, что это одно значение 'x', для которого' f (x) = x' для некоторой функции 'f', как это _enough_ или _not enough_ для продолжения разбора? – thwd
Очень хороший вопрос! Рассмотрим для упрощенного примера функцию 'f = (1 :) :: [Int] -> [Int]'. Наименьшая фиксированная точка 'f' - это бесконечные списки единиц и, следовательно, не может быть вычислен * полностью *. Но вам не нужен весь список, чтобы иметь возможность вычислить 'take 5 (fix f)'! Что усложняет тезисы, так это то, что 'Parser' (и' Set', в меньшей степени) не так просты, как '[Int]', поэтому лень не помогает автоматически. Это имеет смысл для вас? – yatima2975
Я думаю, что это делает: 'fixP' получает мне парсер, который производит самый большой набор уникальных деревьев синтаксического анализа, так что' h (p) = p', где 'h' является первым аргументом' fixP' и 'p' мой парсер. Итак, 'h' просто используется для« проверки »того, что данный парсер фактически эквивалентен оригинальному, непроизводительному парсеру? – thwd