【问题标题】:Recursive Fixpoint function in HaskellHaskell 中的递归定点函数
【发布时间】:2019-10-27 07:36:05
【问题描述】:

我正在尝试创建一个函数来查找函数f 的固定点(x = f x 的情况),同时维护值递归调用的列表。我在将递归调用附加到上一个列表的部分遇到问题。到目前为止,这是我的代码 - 谢谢!

fixpointL :: (Int -> Int) -> Int -> [Int]
fixpointL f x | x == (f x) = [x]
              | otherwise = [x ++ fixpointL f (f x)]

【问题讨论】:

    标签: haskell


    【解决方案1】:

    您没有分享究竟是什么不工作 - Haskell(或者更确切地说是 GHC)的编译时错误有时看起来有点难以理解,但如果您知道如何解释它们,它们总是充满了信息。这里的错误是:

    <interactive>:4:30: error:
        * Couldn't match expected type `[Int]' with actual type `Int'
        * In the first argument of `(++)', namely `x'
          In the expression: x ++ fixpointL f (f x)
          In the expression: [x ++ fixpointL f (f x)]
    
    <interactive>:4:30: error:
        * Couldn't match expected type `Int' with actual type `[Int]'
        * In the expression: x ++ fixpointL f (f x)
          In the expression: [x ++ fixpointL f (f x)]
          In an equation for `fixpointL':
              fixpointL f x
                | x == (f x) = [x]
                | otherwise = [x ++ fixpointL f (f x)]
    

    如您所见,类型不匹配有两个不同的错误 - 但它们实际上密切相关。两者都非常清楚到底出了什么问题。

    在第一个中,它抱怨x ++ fixpointL f (f x) 中的x 应该是[Int]Ints 的列表),但实际上是Int。这是有道理的——x 是函数的第二个参数,类型签名声明它是Int。但是您正在尝试对其应用 (++) 运算符,并且此运算符的类型为:

    Prelude> :t (++)
    (++) :: [a] -> [a] -> [a]
    
    • 也就是说,它接受两个列表(可以包含任何元素),并返回另一个相同类型的列表。 Int 肯定不是一个列表,所以难怪 GHC 无法理解这一点。

    至于第二个错误 - 它告诉您表达式 x ++ fixpointL f (f x) 应该是 Int 但实际上是一个列表 ([Int])。后者是因为它本质上是在假装您将修复第一个错误并将x 变成它所期望的[Int] - 那么表达式x ++ fixpointL f (f x) 确实是Ints 的列表(即@ 987654339@)。但是您已将其括在方括号中:

    [x ++ fixpointL f (f x)]
    

    这意味着一个仅包含一个元素的列表,该元素为x ++ fixpointL f (f x),正如我刚刚解释的那样,目前假定其类型为[Int]。但是由于这是您的函数的返回值,并且您已将返回类型声明为[Int],这意味着[...] 的内容必须是零个或多个@ 类型的值(在本例中为一个) 987654345@。这就解释了另一种类型的不匹配。

    我们如何修复它们?似乎您想要获取 fixpointL f (f x) 的输出 - 我们假设它是 Ints 的列表(您的类型签名说是,所以这是 GHC 必须假设的),并附加开始值,x,放在它的前面。正确的写法,假设你想使用(++)操作符,是这样的:

    [x] ++ fixPointL f (f x)
    

    这将获取仅包含x 的单例列表和递归调用的结果,并将它们放在一个列表中。这会按预期进行类型检查、编译和工作:

    Prelude> fixpointL (^2) 1
    [1]
    Prelude> fixpointL (*2) 1
    [1,2,4,8,16,32,64,128,256,512,...]
    

    (显然,在第二个示例中,我尽可能快地杀死了它,在我可以之前的实际输出要长得多,但你明白了。)

    您可以改进函数的最后一种方法是使用“cons”运算符(:),其目的是在列表的开头添加一个元素,而无需先构造一个单例列表。由于 Haskell 的列表是简单的链表,其中每个元素都包含指向下一个元素的指针,因此这始终是一种有效的操作(与将项目附加到末尾相反,这需要与列表长度成正比的时间)。虽然这里的性能差异很小,但它仍然被认为更惯用。所以这是工作的惯用版本:

    fixpointL :: (Int -> Int) -> Int -> [Int]
    fixpointL f x | x == (f x) = [x]
                  | otherwise = x : fixpointL f (f x)
    

    【讨论】:

      【解决方案2】:

      检查类型:

      x :: Int
      fixpointL f (f x) :: [Int]
      (++) :: [a] -> [a] -> [a]
      
      x ++ fixpointL f (f x) :: ??????????
      
      [????] :: ?????
      

      该行应该是x : fixpointL f (f x)

      请记住,我还没有测试过,你能发布一个你想测试的功能吗?

      【讨论】:

        猜你喜欢
        • 2023-04-09
        • 1970-01-01
        • 1970-01-01
        • 2011-02-14
        • 2019-02-28
        • 1970-01-01
        • 2016-04-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多