【问题标题】:A recursion scheme from Int -> Int?来自 Int -> Int 的递归方案?
【发布时间】:2019-09-24 11:34:23
【问题描述】:

文件夹标识是

foldr (:) []

更一般地说,使用折叠,您可以破坏结构并最终得到一个汇总值,或者以这样一种方式注入结构,最终得到相同的输出结构。

[Int] -> [Int] 要么 [Int] -> Int 要么 [Int] -> ?

我想知道展开器/l 是否有类似的身份。

我知道怎么弄

Int -> [Int]

带有展开/ana。

我正在寻找某种方式从

Int -> Int

使用递归方案。

【问题讨论】:

  • foldr (:) []对应的身份是unfoldr uncons。但是你在寻找什么样的Int -> Int 函数?那里没有可以构造、破坏或以其他方式递归的结构。
  • @user11228628 如果 OP 使用递归定义的自然数,那么将有一个展开并非完全微不足道,尽管我不确定这样做与这个特定问题有多大的相关性。
  • 我正在寻找可以基于输入整数递归并返回整数的东西。类似于使用递归方案的事实。
  • 阶乘 0 = 1 阶乘 n = n * 阶乘 (n - 1)
  • 您可以使用hylomorphism 编写阶乘;你问到底该怎么做?这个问题似乎与是否可以使用展开l/r 定义恒等函数无关,因为阶乘不是恒等函数。

标签: haskell recursion-schemes catamorphism anamorphism


【解决方案1】:

处理构建中间结构并将其拆除以使该结构不会出现在输入或输出中的递归方案是一种亚型,在recursion-schemes 中拼写为hylo

要使用亚同态,您需要指定一个代数(使用递归结构的一个步骤的东西)和一个余代数(产生递归结构的一个步骤的东西),并且您需要有一个数据类型当然是你使用的那种结构。

您建议使用阶乘,所以让我们研究一下如何将其写为亚同态。

查看阶乘的一种方法是作为从初始n 开始倒数的数字列表的乘积。在这个框架中,我们可以将产品视为我们的代数,一次拆下一个缺点列表,将倒计时视为我们的代数,将列表构建为 n 递减。

recursion-schemes 为我们提供了ListF 作为列表的方便基础仿函数,因此我们将使用它作为由代数生成并由代数使用的数据类型。它的构造函数是 NilCons,它们当然类似于完整列表的构造函数,除了 ListF 与递归方案中的任何基本结构一样,在列表将使用实际递归的地方使用类型参数(意思是Cons :: a -> b -> ListF a b 而不是(:) :: a -> [a] -> [a])。

所以这决定了我们的类型。现在定义fact 是一个填空题:

import Prelude hiding (product)
import Data.Functor.Foldable

product :: ListF Int Int -> Int
product Nil = 1
product (Cons a b) = a * b

countDown :: Int -> ListF Int Int
countDown 0 = Nil
countDown n = Cons n (n - 1)

fact :: Int -> Int
fact = hylo product countDown

【讨论】:

    【解决方案2】:

    从您关于阶乘的评论中可以看出,自然数可以被视为递归数据结构:

    data Nat = Zero | Succ Nat
    

    recursion-schemes 机制而言,相应的基本函子将是:

    data NatF a = ZeroF | SuccF a
        deriving (Functor)
    

    NatF 然而,与Maybe 同构。既然如此,recursion-schemes 方便地使Maybe 成为the Natural type from base 的基本函子。例如,这里是ana 的类型,专门用于Natural

    ana @Natural :: (a -> Maybe a) -> a -> Natural
    

    我们可以用它来写Natural的身份展开:

    {-# LANGUAGE LambdaCase #-}
    
    import Numeric.Natural
    import Data.Functor.Foldable
    
    idNatAna :: Natural -> Natural
    idNatAna = ana $ \case
        0 -> Nothing
        x -> Just (x - 1)
    

    我们刚刚给ana 的代数是project for Naturalproject 是展开一层递归结构的函数。在 recursion-schemes 词汇方面,ana project 是身份展开,cata embed 是身份折叠。 (特别是,列表的project 是来自Data.Listuncons,只不过它是用ListF 而不是Maybe 编码的。)

    顺便说一下,阶乘函数可以表示为自然数上的一个变形 (as pointed out in the note at the end of this question)。我们也可以用 recursion-schemes 来实现它:

    fact :: Natural -> Natural
    fact = para $ \case
        Nothing -> 1
        Just (predec, prod) -> prod * (predec + 1)
    

    para 在每个递归步骤中都可以使用要折叠的结构的其余部分(如果我们折叠一个列表,那将是它的尾部)。在这种情况下,我将由此提供的值称为predec,因为在n-th 从下到上的递归步骤predecn - 1

    请注意,user11228628's hylomorphism 可能是一个更有效的实现,如果您碰巧关心的话。 (不过,我还没有对它们进行基准测试。)

    【讨论】:

      猜你喜欢
      • 2017-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-08
      • 1970-01-01
      相关资源
      最近更新 更多