我具有以下标识,该标识(隐式)定义了正整数的分区数(即,将整数写为有序正非零整数之和的方式的数目):

一些注意事项:

  1. Flajolet和Sedjewick在《分析组合》一书中对此进行了研究,该公式的图像是从那里获取的,因为stackoverflow不支持LaTeX。

  2. sigma是一个数的除数之和

我想编写一个haskell程序,用P系数计算一个列表。 第i个项取决于所有先前的项(是对sigma和先前的P进行压缩后得到的列表的总和)。 这个问题是一个很好的例子,说明了如何像Gibbons在他的论文中所写的那样,根据程序的规范“计算”程序。

问题是:是否存在捕获这种计算的已知递归方案? 列表中的每个术语都取决于所有先前术语的计算(并且结果与先前术语没有关系,我的意思是,您必须对每个术语都进行一次新遍历)

===============>>#1 票数:8 已采纳

法定演算警告。 这个问题的基本答案涉及专门的标准递归方案。 但是我有点牵强附会。 当我试图将相同的方法应用于列表以外的结构时,事情变得更加抽象。 我最终接触了艾萨克·牛顿(Isaac Newton)和拉尔夫·福克斯(Ralph Fox),并在此过程中设计了alopegmorphism ,这可能是新东西。

但是无论如何,应该存在某种东西。 它看起来像是变形或“展开”的特例。 让我们先从什么叫做unfoldr在库中。

unfoldr :: (seed -> Maybe (value, seed)) -> seed -> [value]

它显示了如何使用称为Coalgebra的函数反复从种子中生长出一系列值。 在每个步骤中,合并代数都会说是否以[]停止还是通过将值包含在从新种子生成的列表中继续进行。

unfoldr coalg s = case coalg s of
  Nothing       -> []
  Just (v, s')  -> v : unfoldr coalg s'

在这里, 种子类型可以是您喜欢的任何类型-适合于展开过程的任何本地状态。 种子的一个完全明智的概念就是“到目前为止的列表”,也许是相反的顺序,以使最近添加的元素最接近。

growList :: ([value] -> Maybe value) -> [value]
growList g = unfoldr coalg B0 where
  coalg vz = case g vz of   -- I say "vz", not "vs" to remember it's reversed
    Nothing  -> Nothing
    Just v   -> Just (v, v : vz)

在每个步骤中,我们的g操作都会查看我们已经拥有的值的上下文,并决定是否添加另一个值:如果是,则新值既成为列表的开头,又成为新上下文中的最新值。

因此,此growList在每一步将您先前的结果列表交给您,准备好使用zipWith (*) 逆转对于卷积来说非常方便,所以也许我们正在研究类似

ps = growList $ \ pz -> Just (sum (zipWith (*) sigmas pz) `div` (length pz + 1))
sigmas = [sigma j | j <- [1..]]

也许?

递归方案? 对于列表,我们有一个特殊的变形示例,其中种子是到目前为止构建的上下文,一旦我们说了如何构建更多内容,我们就知道如何以相同的方式扩展上下文令牌。 不难看出列表如何工作。 但是,它一般如何适用于变形? 这是毛茸茸的地方。

我们建立可能的无限值,其节点形状由某个函子f给出(当我们“打结”时,其参数变成“子结构”)。

newtype Nu f = In (f (Nu f))

在变形中,结对子使用种子选择最外层节点的形状,并在子结构中填充种子。 (共)递归地,我们绘制变形图,将这些种子生长为子结构。

ana :: Functor f => (seed -> f seed) -> seed -> Nu f
ana coalg s = In (fmap (ana coalg) (coalg s))

让我们从ana重构unfoldr 我们可以从Nu和几个简单的部分中构建许多普通的递归结构: 多项式Functor kit

newtype  K1 a      x  = K1 a                  -- constants (labels)
newtype  I         x  = I x                   -- substructure places
data     (f :+: g) x  = L1 (f x) | R1 (g x)   -- choice (like Either)
data     (f :*: g) x  = f x :*: g x           -- pairing (like (,))

Functor实例

instance Functor (K1 a) where fmap f (K1 a) = K1 a
instance Functor I      where fmap f (I s) = I (f s)
instance (Functor f, Functor g) => Functor (f :+: g) where
  fmap h (L1 fs) = L1 (fmap h fs)
  fmap h (R1 gs) = R1 (fmap h gs)
instance (Functor f, Functor g) => Functor (f :*: g) where
  fmap h (fx :*: gx) = fmap h fx :*: fmap h gx

对于value列表,节点形状函子为

type ListF value = K1 () :+: (K1 value :*: I)

意思是“无聊的标签(对于nil)或value标签和子列表的(对)对”。 ListF value的类型变为

seed -> (K1 () :+: (K1 value :*: I)) seed

是同构的(通过“评估” seed处的多项式ListF value )来

seed -> Either () (value, seed)

这只是头发的宽度

seed -> Maybe (value, seed)

该文件unfoldr期望的。 您可以像这样恢复普通列表

list :: Nu (ListF a) -> [a]
list (In (L1 _))                = []
list (In (R1 (K1 a :*: I as)))  = a : list as

现在,我们如何增长一些一般的Nu f 一个好的开始是为最外面的节点选择形状 f ()类型的值仅给出节点的形状,在子结构位置中有一些琐碎的桩。 确实,要种树,我们基本上需要某种方法来选择“下一个”节点形状,这要知道我们要去的地方以及到目前为止已经完成的工作。 我们应该期待

grow :: (..where I am in a Nu f under construction.. -> f ()) -> Nu f

请注意,对于增长列表,我们的step函数返回ListF value () ,它与Maybe value同构。

但是,我们如何表达到目前为止我们在Nu f 我们将从结构的根开始进入多个节点,因此我们应该期望有很多层。 每一层都应该告诉我们(1)它的形状,(2)我们当前所在的位置,以及(3)已经在该位置左侧构建的结构,但是我们希望在我们所处的位置仍然有桩头还没到 换句话说,这是我在2008年的POPL论文中有关“ 小丑”和“小丑 ”的解剖结构的一个示例。

解剖运算符将仿函数f (视为元素容器)转换为带有两个不同类型元素的双歧Diss f ,在元素内“游标位置”的左侧(小丑)和右侧(小丑)的元素。 f结构。 首先,让我们拥有Bifunctor类和一些实例。

class Bifunctor b where
  bimap :: (c -> c') -> (j -> j') -> b c j -> b c' j'

newtype K2 a       c j = K2 a
data    (f :++: g) c j = L2 (f c j) | R2 (g c j)
data    (f :**: g) c j = f c j :**: g c j
newtype Clowns f   c j = Clowns (f c)
newtype Jokers f   c j = Jokers (f j)

instance Bifunctor (K2 a) where
  bimap h k (K2 a) = K2 a
instance (Bifunctor f, Bifunctor g) => Bifunctor (f :++: g) where
  bimap h k (L2 fcj) = L2 (bimap h k fcj)
  bimap h k (R2 gcj) = R2 (bimap h k gcj)
instance (Bifunctor f, Bifunctor g) => Bifunctor (f :**: g) where
  bimap h k (fcj :**: gcj) = bimap h k fcj :**: bimap h k gcj
instance Functor f => Bifunctor (Clowns f) where
  bimap h k (Clowns fc) = Clowns (fmap h fc)
instance Functor f => Bifunctor (Jokers f) where
  bimap h k (Jokers fj) = Jokers (fmap k fj)

请注意, Clowns f是bifunctor,相当于一个仅包含小丑的f结构,而Jokers f则仅具有小丑。 如果您对所有Functor用具的重复操作感到Bifunctor ,而得到Bifunctor用具,那您是对的:当我们抽象化Arity并使用索引集之间的仿函数来工作时,工作量会减少一些,但这完全是另一回事了。 。

让我们将解剖定义为将bifunctor和functor关联的类。

class (Functor f, Bifunctor (Diss f)) => Dissectable f where
  type Diss f :: * -> * -> *
  rightward   ::  Either (f j) (Diss f c j, c) ->
                  Either (j, Diss f c j) (f c)

类型Diss fcj表示一个f结构,在一个元素位置具有“孔”或“光标位置”,在孔的左侧,我们在c具有“小丑”,在位置的右侧,我们具有“小丑” “在j (该术语是从“ Stealer's Wheel”歌曲“ Stuck in Middle with You”中删除的 。)

该类中的关键操作是rightward同构,该同构告诉我们如何从任一位置开始rightward移动一个位置

  • 充满小丑的整个结构的左侧,或
  • 结构中的一个洞,以及一个放入洞中的小丑

到达任一

  • 结构上的一个洞,以及从其中出来的小丑,或者
  • 充满小丑的整个结构的权利。

艾萨克·牛顿(Isaac Newton)喜欢解剖,但他称它们为分开的差异 ,并在实值函数中定义它们,以获取曲线上两点之间的斜率,因此

divDiff f c j  =  (f c - f j) / (c - j)

并且他使用它们对所有旧函数等进行了最佳的多项式近似。 乘上并乘出

divDiff f c j * c - j * divDiff f c j  =  f c - f j

然后通过加到两边来消除减法

f j + divDiff f c j * c  =  f c + j * divDiff f c j

并且您具有rightward同构。

如果查看这些实例,我们可能会对这些事情建立更多的直觉,然后可以回到我们原来的问题。

一个无聊的旧常数的除数为零。

instance Dissectable (K1 a) where
  type Diss (K1 a) = K2 Void
  rightward (Left (K1 a)) = (Right (K1 a))
  rightward (Right (K2 v, _)) = absurd v

如果从左边开始并向右走,我们将跳过整个结构,因为没有元素位置。 如果我们从元素位置开始,那么有人在撒谎!

身份函子只有一个位置。

instance Dissectable I where
  type Diss I = K2 ()
  rightward (Left (I j))       = Left (j, K2 ())
  rightward (Right (K2 (), c)) = Right (I c)

如果从左边开始,我们到达该位置,然后弹出小丑。 推一个小丑,我们在右边完成。

总而言之,结构是继承的:我们只需要正确地进行标记和重新标记即可。

instance (Dissectable f, Dissectable g) => Dissectable (f :+: g) where
  type Diss (f :+: g) = Diss f :++: Diss g
  rightward x = case x of
      Left (L1 fj)      -> ll (rightward (Left fj))
      Right (L2 df, c)  -> ll (rightward (Right (df, c)))
      Left (R1 gj)      -> rr (rightward (Left gj))
      Right (R2 dg, c)  -> rr (rightward (Right (dg, c)))
    where
      ll (Left (j, df)) = Left (j, L2 df)
      ll (Right fc)     = Right (L1 fc)
      rr (Left (j, dg)) = Left (j, R2 dg)
      rr (Right gc)     = Right (R1 gc)

对于产品,我们必须处于一对结构中的某个位置:要么在小丑和小丑之间处于左侧,而在所有小丑之间都处于正确的结构,要么在小丑与小丑之间都处于小丑的中间,而在小丑与小丑之间则我们处于右侧。

instance (Dissectable f, Dissectable g) => Dissectable (f :*: g) where
  type Diss (f :*: g) = (Diss f :**: Jokers g) :++: (Clowns f :**: Diss g)
  rightward x = case x of
      Left (fj :*: gj) -> ll (rightward (Left fj)) gj
      Right (L2 (df :**: Jokers gj), c) -> ll (rightward (Right (df, c))) gj
      Right (R2 (Clowns fc :**: dg), c) -> rr fc (rightward (Right (dg, c)))
    where
      ll (Left (j, df)) gj = Left (j, L2 (df :**: Jokers gj))
      ll (Right fc)     gj = rr fc (rightward (Left gj))  -- (!)
      rr fc (Left (j, dg)) = Left (j, R2 (Clowns fc :**: dg))
      rr fc (Right gc)     = Right (fc :*: gc)

rightward逻辑确保我们按照左侧的结构进行工作,然后,一旦完成,就从右侧开始工作。 标记为(!)的线是中间的关键时刻,我们从左侧结构的右侧出现,然后进入右侧结构的左侧。

休特在数据结构中“左”和“右”光标移动的概念源于可剖析性(如果您用leftward同构来完成rightward同构)。 f导数只是当小丑和小丑之间的差异趋于零时的极限,或者对于我们来说,当光标两边都具有相同种类的东西时,您会得到什么。

此外,如果您将小丑设为零,那么您会得到

rightward :: Either (f x) (Diss f Void x, Void) -> Either (x, Diss f Void x) (f Void)

但是我们可以删除不可能输入的情况

type Quotient f x = Diss f Void x
leftmost :: f x -> Either (x, Quotient f x) (f Void)
leftmost = rightward . Left

这就告诉我们,每个f结构要么具有最左侧的元素,要么根本不具有任何元素,这是我们在学校学到的结果,称为“剩余定理”。 Quotient算子的多元版本是Brzozowski应用于正则表达式的“导数”。

但是我们的特殊情况是Fox的导数(我从Dan Piponi那里得知):

type Fox f x = Diss f x ()

这是f结构的类型,在游标的右边带有存根。 现在我们可以给出一般grow运算符的类型。

grow :: Dissectable f => ([Fox f (Nu f)] -> f ()) -> Nu f

我们的“上下文”是一叠层次结构,每个层次的左侧都有完全增长的数据,右侧是存根。 我们可以直接实现grow ,如下所示:

grow g = go [] where
  go stk = In (walk (rightward (Left (g stk)))) where
    walk (Left ((), df)) = walk (rightward (Right (df, go (df : stk))))
    walk (Right fm)      = fm

当我们到达每个位置时,我们提取的小丑只是一个存根,但它的上下文告诉我们如何扩展堆栈以生长树的子结构,这为我们提供了需要向右移动的小丑。 一旦用树填充了所有存根,就完成了!

但是这里有一个转折: grow并不像变形一样容易表达。 为每个节点的最左侧子节点提供“种子”是很容易的,因为我们只有右侧的存根。 但是要使下一个种子右边,我们需要的不仅仅是最左边的种子-我们需要从中生长的树! 变形模式要求我们在生长任何子结构之前先为其提供所有种子。 我们的growList是一个变形,仅是因为列表节点最多有一个孩子。

因此,毕竟这是一种新事物,是一无所有的,但是允许给定层的后来的生长依赖于早期的树,而Fox派生词捕捉了“我们尚未工作的地方”的想法。 也许我们应该称它为alopegmorphism ,来自希腊语“λ”的“αλωπηξ”。

===============>>#2 票数:5

如何使用自我参照和懒惰?

假设σ的值在无穷列表sigma ,则

p = [sum (zipWith (*) sigmas (reverse ps)) | ps <- inits p]

会很巧妙地实现此递归。

为了简化代码,在这里我忽略了n的因数,也是因为我不确定P_0应该是什么。

  ask by lando translate from so

未解决问题?本站智能推荐:

2回复

这里计算了多少次(+ 1)?

在我的函数式编程考试中,我有以下问题: 在下面的代码中计算了多少次(+ 1)函数? index函数的定义如下: 我会说6次,但正确的答案似乎是1,我无法理解为什么。 我找不到Haskell中懒惰评估的足够好的解释,所以我想知道什么是正确的答案以及为什么。 先感谢您!
1回复

在Haskell中,使用takeWhile或在此列表理解中使用“常规”不等式有什么区别?

我正在努力学习一个Haskell(非常好),而我正在做的许多不同的事情之一是试图解决一些Project Euler问题,因为我正在测试我的勇气。 在做一些基于Fibonacci的问题时,我偶然发现并开始使用Fibonacci序列的递归无限列表版本: fibs = 1 : 2 : zip
1回复

Haskell将元组列表变成列表列表

我正在尝试将元组列表变成列表列表。 例如,如果我有列表[(9,1), (6,3), (4,1)] ,那么它将变成[[9, 6, 4],[6],[6]] 。 发生的情况是,在元组[(a,b)]的列表中, a表示一个介于0到9之间的数字, b表示该数字的出现, a将始终是唯一的。 我正在尝
2回复

Haskell中的无限列表

我正在尝试定义一个函数,该函数在我的简单语法中创建所有可能单词的无限列表。 但是当我输入head (generate [] []) ,ghci会冻结,尽管head (generate' [] [])可以正常工作(但是我仍然想要一个无限列表)。 有什么问题? UPD:谢谢,我明白了。
2回复

从Haskell列表中删除特定元素

我很难将Haskell和函数式编程结合在一起。 我想做的是操纵一个字符串,以便每次根据给定的数字打印/返回特定字符。 例如: 我已经在网上阅读了很多东西,据我了解,我可以通过过滤和循环来实现这一点,但是我无法理解/理解这种语言的语法来获得一个有效的程序。
4回复

删除语法糖:Haskell中的列表理解

我可以在这个表达式中解释列表理解: 这是输出: 我怎么能用地图,过滤器等编写那段代码? 编辑 这是另一个: 这是输出:
4回复

如何从Haskell中的无限列表中获取元素对?

一般问题 我有一个无限的列表,我想选择一对(a,b) ,其中a和b都来自列表,并且该对满足某些属性。 使用列表推导似乎不起作用,因为列表是无限的。 具体实例 我试图找到一对加起来给定数字的素数(参见此代码高尔夫问题 )。 我已经定义了primes ,这是一个无限的素数列
2回复

是否可以证明所有减少策略中的按需调用具有最小的渐近时间复杂度?

当我在这里阅读Church Rosser II定理时 定理(Church Rosser II) 如果有一个终止减少,那么最外面的减少也将终止。 我想知道:是否有一些定理加强了教会Rosser II定理,以便它告诉渐近时间复杂度而不是终止? 或者,是否可以证明所需减
1回复

通过列表中的指针定义解析器或递归地传递列表会更有效吗?

在C / C ++中,当解析大量字符时,我们使用指针和类似的东西来节省内存。 Haskell会使用相同的内容吗? 我已经看到一些解析器实现在Haskell中接受/返回“待解析的剩余字符”-编译器是否照顾传递的大量内存? 提前致谢!
2回复

文件夹不返回无限列表

我已阅读https://www.haskell.org/haskellwiki/Foldl_as_foldr和其他一些有关foldl和foldr之间差异的博客文章。 现在,我尝试使用文件夹将斐波那契数列编写为无限列表,并提出了以下解决方案: 但是当我take 3 fibs2 ,该函数不