我有t -> Maybe t
类型的各种“部分置换”功能,可以通过返回Just
将我带到数据结构中的新位置,或者如果他们返回Nothing
还不能到那儿。
我经常必须在重复的特定模式中应用这些部分排列,构建所有中间值的列表,但每当我返回到起始位置或排列失败时截断列表。
scan_partial_perms :: Eq t => [t -> Maybe t] -> t -> [t]
scan_partial_perms ps v = map fromJust . takeWhile test $ scanl (>>=) (Just v) ps
where test (Just i) | i /= v = True
test _ = False
iterate_partial_perm = scan_partial_perm . iterate
cycle_partial_perms = scan_partial_perms perms . cycle
我非常确信scanl
在此上下文中具有理想的严格性和尾递归属性。有关优化此代码的其他任何提示吗?特别是,我应该阅读-O3 -fllvm
以外的哪些编译器选项?
在最坏的情况下,我可以使用定义为
的访问器函数替换scanl
和无限列表
perm l i = l !! i `rem` length l
我认为这无法通过正确的优化来提高性能。
答案 0 :(得分:2)
我认为你在scan_partial_perms
中有一个错误,
scan_partial_perms ps v = map fromJust . takeWhile test $ scanl (>>=) (Just v) ps
scanl f s list
始终以s
开头,因此takeWhile test (scanl ...)
为[]
。如果这是故意的,那就太混淆了。假设你想要的是
scan_partial_perms ps v = (v:) . map fromJust . takeWhile test . tail $ scanl (>>=) (Just v) ps
你无能为力。你可以{-# SPECIALISE #-}
,以便为专门的类型消除Eq
字典。如果编译器本身不这样做(如果它可以看到使用站点,那么这对你有好处)。使用ghc> = 7,您可以将其设为{-# INLINABLE #-}
,以便它可以在每个使用网站上进行专门化并可能内联。
我不知道在llvm道路上会发生什么,但在核心级别,map
,fromJust
和takeWhile
尚未内联,所以如果你绝望了如果以后没有在llvm后端内联它们,你可以通过手动内联它们得到几十分之一的百分之几:
scan_partial_perms ps v = v : go v ps
where
go w (q:qs) = case q w of
Just z
| z /= v -> z : go z qs
_ -> []
go _ _ = []
但那些功能非常便宜,所以收益 - 如果存在的话 - 会很小。
所以你拥有的东西已经相当不错了,如果它不够好,你需要一个不同的攻击途径。
列表索引的那个,
perm l i = l !! (i `rem` length l)
-- parentheses necessary, I don't think (l !! i) `rem` length l was what you want
看起来不太好。 length
价格昂贵,(!!)
也很贵,所以一般都应该避免这两种情况。