Párování vzorů - Pattern matching
Ve vědě o počítačích , vyhledávání vzorů je akt kontroly dané sekvenci z žetonů na přítomnost složek nějakého vzoru . Na rozdíl od rozpoznávání vzorů musí být shoda obvykle přesná: „buď to bude, nebo nebude shoda.“ Vzory mají obecně formu sekvencí nebo stromových struktur . Použití shody vzorů zahrnuje výstup umístění umístění (pokud existuje) vzoru v sekvenci tokenů, k výstupu některé komponenty spárovaného vzoru a k nahrazení shody vzoru za nějakou jinou sekvenci tokenů (tj. Hledání a nahrazení ).
Sekvenční vzory (např. Textový řetězec) jsou často popisovány pomocí regulárních výrazů a porovnávány pomocí technik, jako je zpětné sledování .
Stromové vzory se používají v některých programovacích jazycích jako obecný nástroj pro zpracování dat na základě jejich struktury, např. C # , F # , Haskell , ML , Python , Ruby , Rust , Scala , Swift a symbolický matematický jazyk Mathematica mají speciální syntaxi pro vyjádření stromu vzory a jazykový konstrukt pro podmíněné provádění a načítání hodnot na základě toho.
Často je možné dát alternativní vzory, které jsou zkoušeny jeden po druhém, což vede ke konstrukci výkonného podmíněného programování . Párování vzorů někdy zahrnuje podporu stráží .
Parsovací algoritmy se často při transformaci řetězců na syntaxové stromy spoléhají na porovnávání vzorů .
Dějiny
Mezi rané programovací jazyky s konstrukty shody vzorů patří COMIT (1957), SNOBOL (1962), Refal (1968) se stromovým porovnáváním vzorů, Prolog (1972), SASL (1976), NPL (1977) a KRC (1981).
Mnoho textových editorů podporuje porovnávání vzorů různých druhů: editor QED podporuje vyhledávání regulárních výrazů a některé verze TECO podporují při vyhledávání operátor OR.
Systémy počítačové algebry obecně podporují shodu vzorů na algebraických výrazech.
Primitivní vzory
Nejjednodušší vzor při porovnávání vzorů je explicitní hodnota nebo proměnná. Uvažujme například o jednoduché definici funkce v Haskellově syntaxi (parametry funkce nejsou v závorkách, ale jsou odděleny mezerami, = není přiřazení, ale definice):
f 0 = 1
Zde 0 je vzor jedné hodnoty. Nyní, kdykoli je f zadáno 0 jako argument, vzor se shoduje a funkce vrací 1. U jakéhokoli jiného argumentu selže shoda a tím i funkce. Protože syntaxe podporuje alternativní vzory v definicích funkcí, můžeme v definici pokračovat a rozšířit ji tak, aby převzala obecnější argumenty:
f n = n * f (n-1)
Tady je první n
jediný variabilní vzor, který bude odpovídat absolutně jakémukoli argumentu a sváže ho s názvem n, který bude použit ve zbytku definice. V Haskellu (na rozdíl od přinejmenším Hope ) jsou vzory zkoušeny v pořadí, takže první definice stále platí ve velmi specifickém případě vstupu, který je 0, zatímco pro jakýkoli jiný argument se funkce vrací, n * f (n-1)
přičemž n je argument.
Vzor zástupných znaků (často psaný jako _
) je také jednoduchý: stejně jako název proměnné odpovídá libovolné hodnotě, ale neváže hodnotu k žádnému jménu. Algoritmy pro porovnávání zástupných znaků v jednoduchých situacích shody řetězců byly vyvinuty v řadě rekurzivních a nerekurzivních odrůd.
Stromové vzory
Složitější vzory lze sestavit z primitivních vzorů z předchozí části, obvykle stejným způsobem, jako se vytvářejí hodnoty kombinací jiných hodnot. Rozdíl pak spočívá v tom, že u variabilních a zástupných částí se vzor nestaví do jedné hodnoty, ale odpovídá skupině hodnot, které jsou kombinací konkrétních prvků a prvků, které se mohou ve struktuře vzoru lišit .
Vzor stromu popisuje část stromu tak, že začíná uzlem a určuje některé větve a uzly a některé ponechává nespecifikované proměnným nebo zástupným vzorem. Může pomoci přemýšlet o abstraktním stromu syntaxe programovacího jazyka a algebraických datových typech .
V Haskellu následující řádek definuje algebraický datový typ, Color
který má jediný datový konstruktor, ColorConstructor
který zabalí celé číslo a řetězec.
data Color = ColorConstructor Integer String
Konstruktor je uzel ve stromu a celé číslo a řetězec jsou listy ve větvích.
Když chceme zapisovat funkce pro Color
vytvoření abstraktního datového typu , chceme zapsat funkce pro rozhraní s datovým typem, a proto chceme z datového typu extrahovat některá data, například pouze řetězec nebo celočíselnou část Color
.
Pokud předáme proměnnou typu Color, jak můžeme z této proměnné dostat data? Například pro funkci, která získá celočíselnou část Color
, můžeme použít jednoduchý stromový vzor a napsat:
integerPart (ColorConstructor theInteger _) = theInteger
Také:
stringPart (ColorConstructor _ theString) = theString
Vytváření těchto funkcí lze automatizovat pomocí syntaxe datových záznamů Haskella .
Tento příklad Ocaml, který definuje červeno-černý strom a funkci pro jeho opětovné vyvážení po vložení prvku, ukazuje, jak se shodovat ve složitější struktuře generované rekurzivním datovým typem.
type color = Red | Black
type 'a tree = Empty | Tree of color * 'a tree * 'a * 'a tree
let rebalance t = match t with
| Tree (Black, Tree (Red, Tree (Red, a, x, b), y, c), z, d)
| Tree (Black, Tree (Red, a, x, Tree (Red, b, y, c)), z, d)
| Tree (Black, a, x, Tree (Red, Tree (Red, b, y, c), z, d))
| Tree (Black, a, x, Tree (Red, b, y, Tree (Red, c, z, d)))
-> Tree (Red, Tree (Black, a, x, b), y, Tree (Black, c, z, d))
| _ -> t (* the 'catch-all' case if no previous pattern matches *)
Filtrování dat se vzory
K filtrování dat určité struktury lze použít porovnávání vzorů. Například v Haskellu lze pro tento druh filtrování použít porozumění seznamu :
[A x|A x <- [A 1, B 1, A 2, B 2]]
hodnotí na
[A 1, A 2]
Párování vzorů v Mathematice
V Mathematice je jedinou strukturou strom , který je vyplněn symboly. V dosud používané syntaxi Haskell to lze definovat jako
data SymbolTree = Symbol String [SymbolTree]
Příklad stromu by pak mohl vypadat
Symbol "a" [Symbol "b" [], Symbol "c" []]
V tradiční, vhodnější syntaxi jsou symboly psány tak, jak jsou, a úrovně stromu jsou reprezentovány pomocí []
, takže například a[b,c]
je to strom s a jako rodič a b a c jako děti.
Vzor v Mathematice zahrnuje uvedení „_“ na pozice v tomto stromu. Například vzor
A[_]
bude odpovídat prvkům jako A [1], A [2] nebo obecněji A [ x ], kde x je libovolná entita. V tomto případě A
je betonový prvek, zatímco _
označuje kus stromu, který lze měnit. Symbol přidaný k _
navázání shody na název proměnné, zatímco symbol připojený k _
omezení shody na uzly tohoto symbolu. Všimněte si, že i samotné mezery jsou interně reprezentovány jako Blank[]
pro _
a Blank[x]
pro _x
.
Funkce Mathematica Cases
filtruje prvky prvního argumentu, které odpovídají vzoru v druhém argumentu:
Cases[{a[1], b[1], a[2], b[2]}, a[_] ]
hodnotí na
{a[1], a[2]}
Porovnávání vzorů se vztahuje na strukturu výrazů. V níže uvedeném příkladu
Cases[ {a[b], a[b, c], a[b[c], d], a[b[c], d[e]], a[b[c], d, e]}, a[b[_], _] ]
se vrací
{a[b[c],d], a[b[c],d[e]]}
protože pouze tyto prvky budou odpovídat a[b[_],_]
výše uvedenému vzoru .
V Mathematice je také možné extrahovat struktury, které jsou vytvářeny v průběhu výpočtu, bez ohledu na to, jak a kde se objevují. Tuto funkci Trace
lze použít k monitorování výpočtu a vrácení vznikajících prvků, které odpovídají vzoru. Například můžeme definovat Fibonacciho sekvenci jako
fib[0|1]:=1
fib[n_]:= fib[n-1] + fib[n-2]
Potom si můžeme položit otázku: Jaká je posloupnost rekurzivních Fibonacciho volání vzhledem k fib [3]?
Trace[fib[3], fib[_]]
vrací strukturu, která představuje výskyty vzoru fib[_]
ve výpočetní struktuře:
{fib[3],{fib[2],{fib[1]},{fib[0]}},{fib[1]}}
Deklarativní programování
V symbolických programovacích jazycích je snadné mít vzory jako argumenty k funkcím nebo jako prvky datových struktur. Důsledkem toho je schopnost používat vzory k deklarativním výrokům o částech dat a flexibilně instruovat funkce, jak pracovat.
Například funkci MathematicaCompile
lze použít k vytvoření efektivnějších verzí kódu. V následujícím příkladu na detailech zvlášť nezáleží; důležité je, že subexpression {{com[_], Integer}}
instruuje, Compile
že výrazy formuláře com[_]
lze pro účely kompilace považovat za celá čísla :
com[i_] := Binomial[2i, i]
Compile[{x, {i, _Integer}}, x^com[i], {{com[_], Integer}}]
Poštovní schránky v Erlangu také fungují tímto způsobem.
Curry-Howard korespondence mezi důkazy a programy se týká ML -styl porovnávání vzorků na analýzu případu a doklad o vyčerpání .
Párování vzorů a řetězce
Zdaleka nejběžnější forma porovnávání vzorů zahrnuje řetězce znaků. V mnoha programovacích jazycích se určitá syntaxe řetězců používá k reprezentaci regulárních výrazů, což jsou vzory popisující znaky řetězců.
Je však možné provést párování řetězcových vzorů ve stejném rámci, o kterém se hovoří v tomto článku.
Stromové vzory pro řetězce
V Mathematice jsou řetězce reprezentovány jako stromy root StringExpression a všechny znaky v pořadí jako podřízené kořeny. Pro shodu s „libovolným množstvím koncových znaků“ je tedy potřeba nový zástupný znak ___ na rozdíl od _, který by odpovídal pouze jednomu znaku.
V Haskellu a funkčních programovacích jazycích obecně jsou řetězce reprezentovány jako funkční seznamy znaků. Funkční seznam je definován jako prázdný seznam nebo prvek vytvořený na existujícím seznamu. V syntaxi Haskell:
[] -- an empty list
x:xs -- an element x constructed on a list xs
Struktura seznamu s některými prvky je tedy element:list
. Při porovnávání vzorů tvrdíme, že určitá část dat se rovná určitému vzoru. Například ve funkci:
head (element:list) = element
Tvrdíme, že první prvek head
argumentu se nazývá element a funkce to vrátí. Víme, že se jedná o první prvek z důvodu způsobu, jakým jsou seznamy definovány, jediný prvek zkonstruovaný do seznamu. Tento jediný prvek musí být první. Prázdný seznam by vůbec neodpovídal vzoru, protože prázdný seznam nemá hlavu (první vytvořený prvek).
V příkladu nemáme použití list
, takže jej můžeme ignorovat, a tak napsat funkci:
head (element:_) = element
Ekvivalentní transformace Mathematica je vyjádřena jako
head[element, ]:=element
Ukázkové vzory řetězců
Například v Mathematice
StringExpression["a",_]
bude odpovídat řetězci, který má dva znaky a začíná na „a“.
Stejný vzor v Haskellu:
['a', _]
Lze zavést symbolické entity, které představují mnoho různých tříd příslušných funkcí řetězce. Například,
StringExpression[LetterCharacter, DigitCharacter]
bude odpovídat řetězci, který se skládá nejprve z písmene a poté z čísla.
V Haskellu bylo možné použít stráže k dosažení stejných zápasů:
[letter, digit] | isAlpha letter && isDigit digit
Hlavní výhodou manipulace se symbolickými řetězci je, že ji lze zcela integrovat do zbytku programovacího jazyka, místo aby byla samostatnou podjednotkou pro zvláštní účely. Celá síla jazyka může být využita k vytvoření samotných vzorců nebo k analýze a transformaci programů, které je obsahují.
SNOBOL
Snobol ( String orientovaná a symbolického jazyka ) je programovací jazyk vyvinutý mezi 1962 a 1967 u AT & T Bell Laboratories od David J. Farber , Ralph E. Griswold a Ivan P. Polonsky .
SNOBOL4 stojí na rozdíl od většiny programovacích jazyků tím, že má vzory jako prvotřídní datový typ ( tj . Datový typ, s jehož hodnotami lze manipulovat všemi způsoby povolenými pro jakýkoli jiný datový typ v programovacím jazyce) a poskytuje operátorům zřetězení a střídání vzorů . Řetězce generované během provádění lze považovat za programy a spustit.
Koncem 60. a začátkem 70. let byl SNOBOL poměrně široce vyučován na větších amerických univerzitách a v 70. a 80. letech byl široce používán jako jazyk manipulace s textem v humanitních oborech .
Od vytvoření SNOBOLu novější jazyky jako Awk a Perl učinily módní manipulaci s řetězci pomocí regulárních výrazů . Vzory SNOBOL4 však zahrnují gramatiky BNF , které jsou ekvivalentní bezkontextovým gramatikám a výkonnější než regulární výrazy .
Viz také
- AIML pro jazyk AI založený na shodných vzorcích v řeči
- Jazyk AWK
- Coccinelle vzor odpovídá zdrojovému kódu C.
- Odpovídající zástupné znaky
- glob (programování)
- Počet vzorů
- Rozpoznávání vzorů pro fuzzy vzory
- PCRE Perl kompatibilní regulární výrazy, běžná moderní implementace shody řetězcových vzorů přenášených do mnoha jazyků
- REBOL analyzuje dialekt pro porovnávání vzorů používaný k implementaci jazykových dialektů
- Symbolická integrace
- Označená unie
- Tom (jazyk pro porovnávání vzorů)
- SNOBOL pro programovací jazyk založený na jednom druhu shody vzorů
- Vzorový jazyk - metaforický, čerpaný z architektury
- Shoda grafů
Reference
- Kniha Mathematica, kapitola Sekce 2.3: Vzory
- Zpráva Haskell 98, kapitola 3.17 Porovnávání vzorů .
- Referenční příručka Pythonu, kapitola 6.3 Přiřazovací příkazy .
- Čistá Programming Language, kapitola 4.3: Vzory
externí odkazy
- Pohledy: Rozšíření shody vzorů Haskell
- Nikolaas N. Oosterhof, Philip KF Hölzenspies a Jan Kuper. Aplikační vzory . Prezentace na Trends in Functional Programming, 2005
- JMatch : programovací jazyk Java rozšířený o porovnávání vzorů
- ShowTrend : Online porovnávání vzorů pro ceny akcií
- Neúplná historie textového editoru QED Dennise Ritchieho - poskytuje historii regulárních výrazů v počítačových programech
- Implementace funkčních programovacích jazyků, strany 53–103 Simon Peyton Jones, publikováno Prentice Hall, 1987.
- Nemerle, porovnávání vzorů .
- Erlang, porovnávání vzorů .
- Prop: jazyk C ++ založený na porovnávání vzorů, 1999
- PatMat: knihovna pro porovnávání vzorů C ++ založená na SNOBOL / SPITBOL
- Temur Kutsia. Ploché párování . Journal of Symbolic Computation 43 (12): 858–873. Podrobně popisuje ploché shody v Mathematice.
- Jazyk pro přizpůsobení vzorů jazyka EasyPattern pro neprogramátory