Грамматика, которую вы определили, содержит только 2 различных объекта: терминалы (символ) или необязательная часть грамматики.
В приведенном ниже коде на Haskell это отражено в определении дискриминируемого объединения Grammar
.
Первая задача - преобразовать данный конкретный синтаксис (например, «A [B]») в список частей грамматики (каждый терминал или дополнительный). В приведенном ниже коде функция fromString
делает именно это.
Интересная часть, тем не менее, состоит в том, как генерировать все возможные строки для данного синтаксиса. В приведенном ниже коде функция generate
делает это рекурсивно.
Для пустого списка грамматики (который является концом рекурсии), вывод представляет собой одну пустую строку.
Если в списке грамматик в заданной позиции обнаружен конечный символ, соответствующий символ вставляется перед всеми вариантами, сгенерированными из оставшейся части списка грамматики.
Если в списке грамматик найдена необязательная часть, список оставшейся части списка грамматик выводится дважды;один раз с добавленной дополнительной частью и один раз без.
Для нефункциональных людей, читающих это, следует отметить, что fmap
- это функция, которая отображает список в другой список. (стихийно). Другая функция, которую я использовал в приведенном ниже коде, на которую могут наткнуться не-хаскелеры, это concat
, которая превращает список списков в список.
data Grammar = Terminal Char | Optional [Grammar] deriving (Show,Eq)
fromString :: String -> [Grammar] -> ([Grammar],String)
fromString [] acc = (acc,"")
fromString ('[':cs) acc =
let (o,rest) = fromString cs [] in
fromString rest (acc ++ [Optional o])
fromString (']':cs) acc = (acc,cs)
fromString (c:cs) acc = fromString cs (acc ++ [Terminal c])
generate :: [Grammar] -> [String]
generate [] = [""]
generate ((Terminal c) : parts) = fmap (\s -> c : s) $ generate parts
generate ((Optional gs) : parts) = tails ++ (concat . fmap prependOpts $ tails)
where
tails = generate parts
opts = generate gs
prependOpts :: String -> [String]
prependOpts tail = fmap (\o -> o ++ tail) $ opts
Собираем все вместе в REPL (интерактивная оболочка)), запустив fromString "A[B][C]" []
, например, приводит к:
([Terminal 'A',Optional [Terminal 'B'],Optional [Terminal 'C']],"")
И если мы запустим generate
в приведенном выше списке грамматики (первая часть кортежа), мы получим все наши строки:
generate (fst $ fromString "A[B][C]" [])
["A","AC","AB","ABC"]