haskell——设置定点库?

标签 haskell fixpoint-combinators fixed-point-iteration

我正在寻找一个库,该库将在多个可变参数运算符下计算一组的定点/闭包。例如,

fixwith [(+)] [1]

因为整数应该计算所有的 N(自然数,1..)。我试着写它,但有些东西是缺乏的。这不是很有效,而且我感觉我对多参数函数的处理不是最优雅的。此外,是否可以使用内置 fix 编写函数而不是手动递归?
class OperatorN α β | β -> α where
    wrap_op :: β -> (Int, [α] -> α)

instance OperatorN α (() -> α) where
    wrap_op f = (0, \[] -> f ())

instance OperatorN α (α -> α) where
    wrap_op f = (1, \[x] -> f x)

instance OperatorN α ((α, α) -> α) where
    wrap_op f = (2, \[x, y] -> f (x, y))

instance OperatorN α ((α, α, α) -> α) where
    wrap_op f = (3, \[x, y, z] -> f (x, y, z))

instance OperatorN α ((α, α, α, α) -> α) where
    wrap_op f = (4, \[x, y, z, w] -> f (x, y, z, w))

type WrappedOp α = (Int, [α] -> α)
fixwith_next :: Eq α => [WrappedOp α] -> [α] -> [α]
fixwith_next ops s = List.nub (foldl (++) s (map g ops)) where
    g (0, f) = [f []]
    g (arity, f) = do
        x <- s
        let fx = \xs -> f (x:xs)
        g (arity - 1, fx)
fixwith ops s
    | next <- fixwith_next ops s
    , next /= s
    = fixwith ops next
fixwith _ s = s

例子,
> fixwith [wrap_op $ uncurry (*)] [-1 :: Int]
[-1,1]
> fixwith [wrap_op $ uncurry (*)] [1 :: Int]
[1]
> fixwith [wrap_op $ max 3, wrap_op $ \() -> 0] [1 :: Int]
[1,3,0]

设置版本

虽然我想我只需要弄清楚如何减少计算以使其实际上更快,但这并没有提高性能。
import qualified Control.RMonad as RMonad

class OperatorN α β | β -> α where
    wrap_op :: β -> (Int, [α] -> α)

instance OperatorN α (() -> α) where
    wrap_op f = (0, \[] -> f ())

instance OperatorN α (α -> α) where
    wrap_op f = (1, \[x] -> f x)

instance OperatorN α ((α, α) -> α) where
    wrap_op f = (2, \[x, y] -> f (x, y))

instance OperatorN α ((α, α, α) -> α) where
    wrap_op f = (3, \[x, y, z] -> f (x, y, z))

instance OperatorN α ((α, α, α, α) -> α) where
    wrap_op f = (4, \[x, y, z, w] -> f (x, y, z, w))

type WrappedOp α = (Int, [α] -> α)

fixwith_next :: Ord α => [WrappedOp α] -> Set α -> Set α
fixwith_next ops s = Set.unions $ s : map g ops where
    g (0, f) = RMonad.return $ f []
    g (arity, f) = s RMonad.>>= \x ->
        g (arity - 1, \xs -> f (x:xs))
fixwith' ops s
    | next <- fixwith_next ops s
    , next /= s
    = fixwith' ops next
fixwith' _ s = s
fixwith ops s = Set.toList $ fixwith' ops (Set.fromList s)

设置懒惰的版本

我用了RMonad稍微清理一下,并按照丹尼尔的建议让它变得懒惰。遗憾的是,我认为大部分时间都花在了实际的乘法例程中,所以我没有看到这种变化对性能有任何好处。懒惰虽然很酷。
notin :: Ord α => Set α -> Set α -> Set α
notin = flip Set.difference

class Ord α => OperatorN α β | β -> α where
    next_values :: β -> Set α -> Set α

instance Ord α => OperatorN α (α -> α) where
    next_values f s = notin s $ s RMonad.>>= \x -> RMonad.return (f x)

instance Ord α => OperatorN α (α -> α -> α) where
    next_values f s = s RMonad.>>= \x -> next_values (f x) s

instance Ord α => OperatorN α (α -> α -> α -> α) where
    next_values f s = s RMonad.>>= \x -> next_values (f x) s

instance Ord α => OperatorN α (α -> α -> α -> α -> α) where
    next_values f s = s RMonad.>>= \x -> next_values (f x) s

-- bind lambdas with next_values
fixwith_next :: Ord α => [Set α -> Set α] -> Set α -> Set α
fixwith_next nv_bnd s = Set.unions $ map (\f -> f s) nv_bnd -- bound next values

fixwith' :: Ord α => [Set α -> Set α] -> Set α -> [α]
fixwith' ops s@(fixwith_next ops -> next)
    | Set.size next == 0 = []
    | otherwise = (Set.toList next) ++ fixwith' ops (Set.union s next)
fixwith ops s = (Set.toList s) ++ fixwith' ops s
fixwith_lst ops = fixwith ops . Set.fromList

例子
> take 3 $ fixwith [next_values (+2)] (Set.fromList [1])
[1,3,5]

我不得不失去一元运算,但这不是交易杀手。

最佳答案

不,fix是一条红鲱鱼。它正在计算一种与你不同的定点。

您对 arity 的处理非常务实。有许多不同的方法可以让它少一点样板。见 one of my previous answers一种这样的方式。我相信最终也会有人加入并添加另一个令人兴奋的基于类型级数字的解决方案。 =)

为了提高效率,我不确定您是否可以仅使用 Eq 做得更好反正实例。您可以考虑过滤掉 s (本地)g 调用结果中的值函数——即让fixwith_next只返回新元素。这应该可以使终止检查更快,甚至可以有一个高效、懒惰的 fixwith .

如果您不介意严格要求 Ord例如,使用真实的 Set s 也可能会提高效率。

关于haskell——设置定点库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7250847/

相关文章:

haskell - 在 do 表示法中使用 where 语句

haskell - 通过持久的实体键散列?

c++ - 如何优雅地找到一个简单的mod函数的固定点?

scala - Scala 中的定点

haskell - Haskell 中是否有定点运算符?

haskell - 尝试 happstack-tutorial 时出现问题

file - 如何让我的 Haskell 代码使用惰性和垃圾收集器

haskell - 如何使用修复,它是如何工作的?

haskell - 固定点是什么?

clojure - clojure 中的定点组合器