Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/file/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 记忆以集合为参数的函数_Haskell_Memoization - Fatal编程技术网

Haskell 记忆以集合为参数的函数

Haskell 记忆以集合为参数的函数,haskell,memoization,Haskell,Memoization,我正在使用Data.memombinators()来记忆一个函数,该函数将一个集合作为其参数并返回一个集合(这是一个精心设计的示例,它什么都不做,但需要很长时间才能完成): 由于Data.memo Combinators未实现集合表,因此我想编写自己的: {-# LANGUAGE RankNTypes #-} import Data.MemoCombinators (Memo) import qualified Data.MemoCombinators as Memo import Data.

我正在使用
Data.memombinators
()来记忆一个函数,该函数将一个集合作为其参数并返回一个集合(这是一个精心设计的示例,它什么都不做,但需要很长时间才能完成):

由于
Data.memo Combinators
未实现集合表,因此我想编写自己的:

{-# LANGUAGE RankNTypes #-}

import Data.MemoCombinators (Memo)
import qualified Data.MemoCombinators as Memo
import Data.Set (Set)
import qualified Data.Set as Set

set :: Ord a => Memo a -> ((Set a) -> r) -> (Set a) -> r
set m f = Memo.list m (f . Set.fromList) . Set.toList
这是我的
测试
,应该记住:

test s = set Memo.integral test' s
    where
      test' s = case Set.toList s of
                []     -> Set.singleton 0
                [x]    -> Set.singleton 1
                (x:xs) -> test (Set.singleton x) `Set.union` test (Set.fromList xs)
我不清楚
数据的文档,所以基本上我不知道自己在做什么

我的问题是:

  • Memo.list
    函数的第二个参数是什么?它是列表元素的记忆器吗

  • 如何直接为集合实现表格,而不使用
    Memo.list
    ?下面是我想弄清楚如何在不使用别人的库的情况下手动实现备忘录。例如,使用
    贴图
    。我见过使用无限列表记忆整数的例子,但对于映射,我不知道如何初始化映射以及如何插入映射


  • 谢谢您的帮助。

    您的
    测试功能很好,不过通常您会使用Set操作将
    测试定义为Set上的函数。下面是我所说的一个例子:

    -- memoize a function on Set Int
    foo = set M.integral foo'
      where foo' s | Set.null s = 0
            foo' s = let a = Set.findMin s
                         b = Set.findMax s
                         m = (a+b) `div` 2
                         (lo,found,hi) = Set.splitMember m s
                     in if a >= b
                          then 1
                          else (if found then 1 else 0) + foo lo + foo hi
    
    这是一种计算集合中元素数量的非常低效的方法,但请注意,
    foo'
    是如何根据集合操作定义的

    还有其他问题:

  • Memo.list函数的第二个参数是什么?它是列表元素的记忆器吗
  • Memo.list
    具有签名
    Memo a->Memo[a]
    ,因此在表达式
    Memo.list mf
    中我们有:

    m :: Memo a
    f :: [a] -> r    -- some type r
    Memo.list m f :: [a] -> r
    
    因此,
    f
    是您正在记忆的
    [a]
    上的函数,
    m
    是一个函数的记忆器,函数的参数类型为
    a

  • 如何直接为集合实现表
  • 这取决于你所说的“直接”是什么意思。以这种方式进行记忆将涉及到创建一个(可能是无限的)惰性数据结构。
    字符串
    积分
    列表
    备忘录都使用某种形式的惰性trie。这与命令式语言中的记忆非常不同,在命令式语言中,您显式检查哈希映射以查看是否已经计算了某些内容,并使用函数的值更新该哈希映射,等等。(顺便说一下,您可以在ST或IO单元格中进行这种记忆,它甚至比数据更好。 通过传递到列表来记忆
    设置->r
    函数是一个好主意,但我会使用to/from AscList:

    set m f = Memo.list m (f . Set.fromAscList) . Set.toAscList
    
    这样,set
    set.fromList[3,4,5]
    将重新使用trie的同一部分,该部分是为记忆set.fromList[3,4]
    的值而创建的

  • Memo.list函数的第二个参数是什么?它是列表元素的记忆器吗
  • 第一个参数
    m
    是列表元素的记忆器。第二个参数
    f
    是要应用于列表的函数(也将被记忆)

  • 如何在不使用Memo.list的情况下直接为集合实现一个表?下面是我们想要了解的实现方法 在不使用某人的库的情况下手动记录。例如, 使用映射。我见过使用 无限列表,但在地图的情况下,我不知道如何 初始化地图以及如何插入地图
  • 使用与
    Data.memo combinator
    相同的策略,您可以执行与列表类似的操作。这种方法不使用显式数据结构,而是探索Haskell将内容保存在内存中的方式和惰性计算

    set :: Ord a => Memo a -> Memo (Set a)
    set m f = table (f Set.empty) (m (\x -> set m (f . (x `Set.insert`))))
      where
      table nil cons set | Set.null set = nil
                         | otherwise    = uncurry cons (Set.deleteFindMin set)
    
    您还可以在Haskell中使用显式数据结构(如
    映射
    )来使用memonization。我将使用Fibonacci示例来演示这一点,因为它更容易进行基准测试,但对于其他函数也类似

    让我们从简单的实现开始:

    fib0 :: Integer -> Integer
    fib0 0 = 0
    fib0 1 = 1
    fib0 x = fib0 (x-1) + fib0 (x-2)
    
    import qualified Data.MemoCombinators as Memo
    
    fib1 :: Integer -> Integer
    fib1 = Memo.integral fib'
      where
      fib' 0 = 0
      fib' 1 = 1
      fib' x = fib1 (x-1) + fib1 (x-2)
    
    然后提出了这一实施方案:

    fib0 :: Integer -> Integer
    fib0 0 = 0
    fib0 1 = 1
    fib0 x = fib0 (x-1) + fib0 (x-2)
    
    import qualified Data.MemoCombinators as Memo
    
    fib1 :: Integer -> Integer
    fib1 = Memo.integral fib'
      where
      fib' 0 = 0
      fib' 1 = 1
      fib' x = fib1 (x-1) + fib1 (x-2)
    
    最后,我的版本使用
    Map

    import Data.Map (Map)
    import qualified Data.Map as Map
    
    fib2 :: Integer -> Integer
    fib2 = fst . fib' (Map.fromList [(0, 0),(1, 1)])
      where
      fib' m0 x | x `Map.member` m0 = (Map.findWithDefault 0 x m0, m0)
                | otherwise         = let (v1, m1) = fib' m0 (x-1)
                                          (v2, m2) = fib' m1 (x-2)
                                          y = v1 + v2
                                      in (y, Map.insert x y m2)
    
    现在,让我们看看他们的表现:

    fib0 40: 13.529371s
    fib1 40: 0.000121s
    fib2 40: 0.000048s
    
    fib0
    已经太慢了。让我们用另外两个做一个适当的测试:

    fib1 400000: 6.234243s
    fib2 400000: 4.022798s
    
    fib1 500000: 8.683649s
    fib2 500000: 5.781104s
    
    对于我执行的所有测试,
    Map
    解决方案似乎实际上比
    Memo
    解决方案的性能要好。但我认为
    数据的最大优势。MemoCombinators
    实际上具有如此优异的性能,而无需编写比原始解决方案多得多的代码



    更新:我更改了结论,因为我没有正确执行基准测试。我在同一执行中执行了几个调用,在500000的情况下,无论第二个调用是什么(无论是
    fib1
    还是
    fib2
    ),这花费的时间太长。

    您的
    fib
    示例很好,但请注意,创建的映射在调用
    fib2
    之间没有保留。此外,您应该尝试使用
    Data.IntMap
    来查看它的性能是否优于
    Data.map