Ete pase a mwen te patisipe nan
Kòm yon patisipan nan Google Summer of Code 2019, mwen te fè yon pwojè nan bibliyotèk la
Nan pòs sa a mwen pral pale sou aplikasyon mwen an nan yon algorithm pou tcheke yon graf pou bipartiteness nan Haskell. Menm si algorithm la se youn nan pi fondamantal la, mete ann aplikasyon li trè byen nan yon style fonksyonèl te pran m 'plizyè iterasyon e li te mande anpil travay. Kòm yon rezilta, mwen rezoud sou yon aplikasyon ak transfòmatè monad.
Sou tèt mwen
Non mwen se Vasily Alferov, mwen se yon etidyan katriyèm ane nan St. Petersburg HSE. Byen bonè nan blog la mwen te ekri
Konsènan aplikasyon algorithm la
Avètisman
Nou ankouraje elèv k ap patisipe nan pwogram nan pou yo blog. Yo te ban mwen yon platfòm pou blog la
Ou ka jwenn Pull Request ak kòd nan kesyon an
Ou ka li sou rezilta travay mwen an (an angle)
Pòs sa a sipoze ke lektè a abitye ak konsèp debaz yo nan pwogramasyon fonksyonèl, byenke mwen pral eseye sonje tout tèm yo itilize lè lè a rive.
Tcheke graf pou bipatiteness
Yon algorithm pou tcheke yon graf pou bipartiteness anjeneral yo bay nan yon kou sou algoritm kòm youn nan algorithm graf ki pi senp. Lide li se senp: premye yon jan kanmenm mete somè nan pati gòch la oswa dwa, epi lè yo jwenn yon kwen konfli, nou afime ke graf la pa bipati.
Yon ti kras plis detay: premye nou mete kèk somè nan pataje gòch la. Li evidan, tout vwazen yo nan somè sa a dwe kouche nan lòb dwat la. Pli lwen, tout vwazen vwazen yo nan somè sa a dwe kouche nan lòb gòch la, ak sou sa. Nou kontinye plase aksyon nan somè toutotan gen toujou somè nan eleman ki konekte nan somè nou te kòmanse avèk ke nou pa te asiyen vwazen yo. Lè sa a, nou repete aksyon sa a pou tout eleman ki konekte.
Si gen yon kwen ant somè ki tonbe nan menm patisyon an, li pa difisil pou jwenn yon sik enpè nan graf la, ki se lajman li te ye (ak byen evidamman) enposib nan yon graf inite. Sinon, nou gen yon patisyon kòrèk, ki vle di graf la se toule de pati.
Tipikman, algorithm sa a aplike lè l sèvi avèk
Kidonk, nou rive nan konplo sa a. Nou travèse somè graf la lè l sèvi avèk rechèch pwofondè-premye epi bay yo aksyon, chanje kantite pataje a pandan n ap deplase sou kwen an. Si nou eseye bay yon pati nan yon somè ki deja gen yon pataje asiyen, nou ka di san danje ke graf la pa bipati. Moman tout somè yo asiyen yon pati epi nou te gade tout bor yo, nou gen yon bon patisyon.
Pite nan kalkil
Nan Haskell nou sipoze ke tout kalkil yo pwòp. Sepandan, si sa a te vrèman ka a, nou pa ta gen okenn fason yo enprime anyen sou ekran an. Nan tout, pwòp kalkil yo tèlman parese ke pa gen youn pwòp rezon pou kalkile yon bagay. Tout kalkil ki fèt nan pwogram nan yo yon jan kanmenm fòse nan "enpwòp" monad IO.
Monad yo se yon fason yo reprezante kalkil ak efè nan Haskell. Eksplike ki jan yo travay pi lwen pase sijè ki abòde lan nan pòs sa a. Ou ka li yon bon deskripsyon klè nan lang angle
Isit la mwen vle fè remake ke pandan ke kèk monad, tankou IO, yo aplike nan majik du, prèske tout lòt yo aplike nan lojisyèl ak tout kalkil nan yo se pi bon kalite.
Gen yon anpil nan efè ak chak gen pwòp monad pa yo. Sa a se yon teyori trè fò ak bèl: tout monad aplike koòdone nan menm. Nou pral pale sou twa monad sa yo:
- Swa ea se yon kalkil ki retounen yon valè nan kalite a oswa voye yon eksepsyon nan kalite e. Konpòtman monad sa a se trè menm jan ak manyen eksepsyon nan lang enperatif: erè yo ka kenbe oswa pase sou. Diferans prensipal la se ke monad la se konplètman lojikman aplike nan bibliyotèk la estanda nan Haskell, pandan y ap lang enperatif anjeneral itilize mekanis sistèm opere.
- Eta sa a se yon kalkil ki retounen yon valè kalite a epi ki gen aksè a eta ki ka chanje kalite s.
- Petèt yon. Monad Petèt la eksprime yon kalkil ki ka entèwonp nenpòt ki lè lè yo pa retounen Anyen. Sepandan, nou pral pale sou aplikasyon an nan klas MonadPlus pou kalite a Petèt, ki eksprime efè opoze a: li se yon kalkil ki ka entèwonp nan nenpòt ki lè pa retounen yon valè espesifik.
Aplikasyon algorithm la
Nou gen de kalite done, Graph a ak Bigraph ab, premye nan yo ki reprezante graf ki gen somè ki make ak valè kalite a, epi dezyèm lan reprezante graf inite ak somè bò gòch ki make ak valè ki gen kalite a ak dwa. -bò somè ki make ak valè kalite b.
Sa yo pa kalite ki soti nan bibliyotèk Alga. Alga pa gen yon reprezantasyon pou graf bipati san direksyon. Mwen te fè kalite yo tankou sa a pou klè.
Nou pral bezwen tou fonksyon asistan ak siyati sa yo:
-- Список соседей данной вершины.
neighbours :: Ord a => a -> Graph a -> [a]
-- Построить двудольный граф по графу и функции, для каждой вершины
-- выдающей её долю и пометку в новой доле, игнорируя конфликтные рёбра.
toBipartiteWith :: (Ord a, Ord b, Ord c) => (a -> Either b c)
-> Graph a
-> Bigraph b c
-- Список вершин в графе
vertexList :: Ord a => Graph a -> [a]
Сигнатура функции, которую мы будем писать, выглядит так:
type OddCycle a = [a]
detectParts :: Ord a => Graph a -> Either (OddCycle a) (Bigraph a a)
Li fasil pou wè ke si pandan rechèch la pwofondè-premye nou jwenn yon kwen konfli, sik la enpè kouche sou tèt pil rekursion la. Kidonk, pou retabli li, nou bezwen koupe tout bagay soti nan pil rekursion jiska premye ensidan an nan dènye somè an.
Nou aplike rechèch pwofondè-premye lè nou kenbe yon seri asosyasyon nimewo pataje pou chak somè. Pile repetisyon an pral otomatikman konsève atravè aplikasyon klas Functor nan monad nou te chwazi a: nou pral sèlman bezwen mete tout somè yo soti nan chemen an nan rezilta a retounen nan fonksyon an recursive.
Premye lide m 'te sèvi ak Monad la Swa, ki sanble aplike egzakteman efè yo ke nou bezwen. Premye aplikasyon mwen te ekri a te trè pre opsyon sa a. An reyalite, mwen te gen senk aplikasyon diferan nan yon pwen epi evantyèlman rezoud sou yon lòt.
Premyèman, nou bezwen kenbe yon seri asosyasyon idantifyan pataje - sa a se yon bagay sou Leta. Dezyenmman, nou bezwen kapab aret ler en konfli i ganny detekte. Sa a ka swa Monad pou Swa, oswa MonadPlus pou Petèt. Diferans prensipal la se ke Swa ka retounen yon valè si kalkil la pa te sispann, epi Petèt retounen sèlman enfòmasyon sou sa a nan ka sa a. Depi nou pa bezwen yon valè separe pou siksè (li deja estoke nan Eta), nou chwazi Petèt. Ak nan moman sa a lè nou bezwen konbine efè yo nan de monad, yo soti
Poukisa mwen te chwazi yon kalite konplèks konsa? De rezon. Premyèman, aplikasyon an sanble trè sanble ak enperatif. Dezyèmman, nou bezwen manipile valè a retounen nan ka ta gen konfli lè retounen tounen soti nan rekursion retabli bouk la enpè, ki se pi fasil fè nan Monad la Petèt.
Se konsa nou jwenn aplikasyon sa a.
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE ScopedTypeVariables #-}
data Part = LeftPart | RightPart
otherPart :: Part -> Part
otherPart LeftPart = RightPart
otherPart RightPart = LeftPart
type PartMap a = Map.Map a Part
type OddCycle a = [a]
toEither :: Ord a => PartMap a -> a -> Either a a
toEither m v = case fromJust (v `Map.lookup` m) of
LeftPart -> Left v
RightPart -> Right v
type PartMonad a = MaybeT (State (PartMap a)) [a]
detectParts :: forall a. Ord a => Graph a -> Either (OddCycle a) (Bigraph a a)
detectParts g = case runState (runMaybeT dfs) Map.empty of
(Just c, _) -> Left $ oddCycle c
(Nothing, m) -> Right $ toBipartiteWith (toEither m) g
where
inVertex :: Part -> a -> PartMonad a
inVertex p v = ((:) v) <$> do modify $ Map.insert v p
let q = otherPart p
msum [ onEdge q u | u <- neigbours v g ]
{-# INLINE onEdge #-}
onEdge :: Part -> a -> PartMonad a
onEdge p v = do m <- get
case v `Map.lookup` m of
Nothing -> inVertex p v
Just q -> do guard (q /= p)
return [v]
processVertex :: a -> PartMonad a
processVertex v = do m <- get
guard (v `Map.notMember` m)
inVertex LeftPart v
dfs :: PartMonad a
dfs = msum [ processVertex v | v <- vertexList g ]
oddCycle :: [a] -> [a]
oddCycle c = tail (dropWhile ((/=) last c) c)
Blòk kote a se nwayo algorithm la. Mwen pral eseye eksplike sa k ap pase andedan li.
- inVertex se yon pati nan rechèch pwofondè-premye kote nou vizite somè a pou premye fwa. Isit la nou bay yon nimewo pataje nan somè a epi kouri onEdge sou tout vwazen yo. Sa a se tou kote nou retabli pil apèl la: si msum te retounen yon valè, nou pouse somè v la.
- onEdge se pati kote nou vizite kwen an. Li rele de fwa pou chak kwen. Isit la nou tcheke si somè lòt bò a te vizite, epi vizite li si se pa sa. Si vizite, nou tcheke si kwen an konfli. Si li se, nou retounen valè a - tèt la anpil nan pil rekursion la, kote tout lòt somè yo pral Lè sa a, dwe mete sou retounen.
- processVertex tcheke pou chak somè si wi ou non li te vizite epi kouri inVertex sou li si se pa sa.
- dfs kouri processVertex sou tout somè.
Se tout.
Istwa mo INLINE
Mo INLINE pa t nan premye aplikasyon algorithm la; li te parèt pita. Lè mwen te eseye jwenn yon pi bon aplikasyon, mwen te jwenn ke vèsyon an ki pa-INLINE te notables pi dousman sou kèk graf. Lè nou konsidere ke semantik fonksyon yo ta dwe travay menm jan an, sa a anpil etone m '. Menm moun lòt nasyon, sou yon lòt machin ak yon vèsyon diferan nan GHC pa te gen okenn diferans aparan.
Apre mwen te pase yon semèn li pwodiksyon GHC Nwayo a, mwen te kapab ranje pwoblèm nan ak yon liy nan INLINE eksplisit. Nan kèk pwen ant GHC 8.4.4 ak GHC 8.6.5 optimisateur la sispann fè sa poukont li.
Mwen pa t espere rankontre salte sa yo nan pwogram Haskell. Sepandan, menm jodi a, optimiseurs pafwa fè erè, epi li nan travay nou yo ba yo sijesyon. Pou egzanp, isit la nou konnen ke fonksyon an ta dwe inline paske li se inline nan vèsyon an enperatif, e sa a se yon rezon ki fè yo bay konpilatè a yon allusion.
Kisa ki te pase apre?
Lè sa a, mwen aplike algorithm Hopcroft-Karp ak lòt monad, e se te fen pwogram nan.
Gras a Google Summer of Code, mwen te genyen eksperyans pratik nan pwogram fonksyonèl, ki non sèlman te ede m jwenn yon estaj nan Jane Street ete annapre a (mwen pa sèten ki jan kote sa a byen li te ye menm nan mitan odyans ki gen konesans Habr, men li se youn. nan kèk kote ou ka ete angaje yo nan pwogramasyon fonksyonèl), men tou, prezante m 'nan mond lan bèl bagay nan aplike paradigm sa a nan pratik, siyifikativman diferan de eksperyans mwen nan lang tradisyonèl yo.
Sous: www.habr.com