我有一个程序性的EDSL,它使用语句块。
这些语句不按特定顺序添加到块中,尽管语句之间可能存在依赖关系。
然而,在EDSL的编译过程中,我需要确保语句是按依赖顺序排序的。
B := A
C := B
E := D因为并非所有语句都有依赖项,所以没有总顺序(例如,上面的E := D是独立的,可以放置在任何地方)。不存在循环依赖关系,因此列表排序应该是可能的。
我试图通过使用Data.List.sortBy并定义Ordering来破解解决方案,这将返回EQ以表示语句没有依赖项。这对一些例子是有效的,但在一般情况下则不然,例如,订购下列命令没有任何作用:
C := B B := A
D := C = should produce => C := B
B := A D := C这是因为默认的排序插入排序,并且只确保插入的项较小或等于下一个。
我在因特网上搜索了Poset实现,但没有发现任何适用的:
altfloat:Data.Poset定义了Ordering = LT | GT | EQ | NC (不可比较的NC),这是很好的,但是提供的sort假设NaN-like不可比较的项目,然后丢弃它们。
logfloat:Data.Number.PartialOrd类似于上面的内容,除了使用Maybe Ordering之外,我在包中没有看到排序函数。
Math.Combinatorics.Poset我还没有弄清楚如何使用它或者它是否适用。
下面是一个包含绑定语句和非绑定语句的最小示例。非二进制语句的顺序很重要,它们必须保持原来的顺序(即排序需要为稳定的 w.r.t。没有依赖关系的语句)。
我希望有一个简单的解决方案,而不用一个完整的依赖图.
module Stmts where
import Data.List ( sortBy )
data Var = A | B | C | D | E | F | G | H deriving (Eq, Show)
data Stmt = Var := Var
| Inc Var
deriving (Show)
-- LHS variable
binds :: Stmt -> Maybe Var
binds (v := _) = Just v
binds _ = Nothing
-- RHS variables
references :: Stmt -> [Var]
references (_ := v) = [v]
references (Inc v) = [v]
order :: [Stmt] -> [Stmt]
order = sortBy orderStmts
orderStmts :: Stmt -> Stmt -> Ordering
orderStmts s1 s2 = ord mbv1 mbv2
where
ord Nothing Nothing = EQ -- No dep since they don't bind vars
ord (Just v1) Nothing = LT -- Binding statements have precedence
ord Nothing (Just v2) = GT -- ^^^
ord (Just v1) (Just v2) -- Both statements are binding:
| v1 `elem` refs2 = LT -- * s2 depends on s1
| v2 `elem` refs1 = GT -- * s1 depends on s2
| otherwise = EQ -- * neither
-- *Maybe* they bind variables
mbv1 = binds s1
mbv2 = binds s2
-- Variables they reference
refs1 = references s1
refs2 = references s2
-- The following should return [B := A, C := B, D := C, Inc F, Inc G]
test = order [Inc F, Inc G, C := B, D := C, B := A]发布于 2014-10-03 12:42:11
您的方法的问题是,您的orderStmts既不是排序也不是偏序。特别是,它不是传递性,这就是为什么尝试使用它进行排序失败的原因。
你要找的是拓扑排序。您有一个顶点(语句)图,在它们之间有边(它们的依赖关系),并且希望确保排序与边匹配。
我将只关注声明,因为非绑定语句很容易(我们只需要将列表分成两部分,对声明进行排序并再次连接)。
拓扑排序已经在Data.Graph中实现,这使得任务非常简单:
module Stmts where
import Data.Graph
data Var = A | B | C | D | E | F | G | H deriving (Eq, Ord, Show)
data Decl = Var := Var
deriving (Show, Eq)
data Stmt = Decl
| Inc Var
deriving (Show, Eq)
sortDecls :: [Decl] -> [SCC Decl]
sortDecls = stronglyConnComp . map triple
where
triple n@(x := y) = (n, x, [y])
-- The following should return [B := A, C := B, D := C]
test = map flattenSCC . sortDecls $ [C := B, D := C, B := A]调用flattenSCC只用于测试,因为SCC没有Show实例。您可能希望检查SCC的循环(循环将是语言编译错误),如果没有,则提取排序的序列。
发布于 2014-10-02 10:50:30
我认为整理你的陈述的唯一方法是从根到孩子。
import Data.List
data Var = A | B | C | D | E | F | G | H deriving (Eq, Show)
data Stmt = Var := Var deriving (Show)
parent :: Stmt -> Var
parent (_ := p) = p
child :: Stmt -> Var
child (c := _) = c
steps :: [Stmt] -> [[Stmt]]
steps st = step roots st
where step _ [] = []
step r s = let (a, b) = partition (flip elem r . parent) s
(v, u) = partition (flip elem (map child b) . child ) a
in if null u then error "Cycle!"
else u : step (r ++ (nub $ map child u)) (v ++ b)
roots = let cs = map child st
rs = nub $ filter (not . flip elem cs) (map parent st)
in if null rs then error "No roots!"
else rs
main = mapM_ print $ steps [F := H, G := H, C := B, D := C, B := A]带输出
[F := H,G := H,B := A]
[C := B]
[D := C]当“排序”结束时,组(不是语句)。
(该代码具有稳定性,因为它通过partition、map、++、.不变量)
(添加)
如果您确实希望获得某种稳定性属性(对语句进行排序),则必须添加一些其他限制(定义“稳定性”)。
让两个“排序”直接算法(简单地将语句重新排序到前面或后面)
orderToFront :: [Stmt] -> [Stmt]
orderToFront [] = []
orderToFront (s@(_ := p):xs) = let (l, r) = splitAtFirst ((==p).child) xs
in if null r then s: orderToFront xs
else head r: s: orderToFront (l ++ tail r)
orderToBack' :: [Stmt] -> [Stmt]
orderToBack' [] = []
orderToBack' (s@(c := _):xs) = let (l, r) = splitAtFirst ((==c).parent) xs
in if null r then s: orderToBack' xs
else orderToBack' (l ++ head r: s: tail r)
orderToBack = reverse . orderToBack'
splitAtFirst :: (a -> Bool) -> [a] -> ([a], [a])
splitAtFirst f xs = let rs = dropWhile (not.f) xs
in (take (length xs - length rs) xs, rs)
main = do
let q = [F := H, C := B, D := C, G := F, B := A]
putStrLn "-- orderToFront"
mapM_ print $ orderToFront q
putStrLn "-- orderToBack"
mapM_ print $ orderToBack q对于相同的输入,orderToFront输出不同于orderToBack输出,但两者都是有效的。
-- orderToFront
F := H
B := A
C := B
D := C
G := F
-- orderToBack
B := A
F := H
G := F
C := B
D := C(只有等式关系,您的算法不能低于O(n^2),但是如果定义了稳定性限制,则可以减少它)
https://stackoverflow.com/questions/26158233
复制相似问题