【问题标题】:Y combinator, Infinite types and Anonymous recursion in HaskellHaskell 中的 Y 组合器、无限类型和匿名递归
【发布时间】:2012-01-08 15:40:17
【问题描述】:

我试图解决maximal subsequence sum problem 并想出了一个neato 解决方案

msss :: (Ord a, Num a) => [a] -> a
msss = f 0 0

f gmax _ [] = gmax
f gmax lmax (x:xs) = 
  let g = max (lmax + x)
  in  f (g gmax) (g 0) xs

您调用包装函数msss,然后调用f,这反过来又实际完成了工作。 解决方案很好,而且 afaik 工作正常。如果由于某种原因我必须解决生产代码中的最大子序列和问题,我会这样做。

然而,这个包装函数真的让我很烦。我喜欢在 haskell 中,如果你足够坚持,你可以将整个程序写在一行上,真正让人们明白程序几乎只是一个大表达式。所以我想我会尝试消除包装函数来应对额外的挑战。

现在我遇到了经典问题:如何进行匿名递归?当你不能给函数命名时,你如何进行递归?值得庆幸的是,计算之父很久以前就通过发现Fixed-Point Combinators 解决了这个问题,其中最受欢迎的是Y Combinator

我已经进行了各种尝试来设置 Y 组合器,但它们无法通过编译器。

msss' :: [Int] -> Int
msss' = (\y f x -> f (y y f) x) 
        (\y f x -> f (y y f) x) 
        (\g' gmax lmax list -> if list == [] 
                               then gmax 
                               else g' (max gmax lmax + head list) 
                                       (max 0    lmax + head list) 
                                       tail list)

只是给

Prelude> :l C:\maxsubseq.hs
[1 of 1] Compiling Main             ( C:\maxsubseq.hs, interpreted )

C:\maxsubseq.hs:10:29:
    Occurs check: cannot construct the infinite type:
      t0 = t0 -> (([Int] -> Int) -> [Int] -> Int) -> [Int] -> Int
    In the first argument of `y', namely `y'
    In the first argument of `f', namely `(y y f)'
    In the expression: f (y y f) x

C:\maxsubseq.hs:11:29:
    Occurs check: cannot construct the infinite type:
      t0 = t0 -> (([Int] -> Int) -> [Int] -> Int) -> [Int] -> Int
    In the first argument of `y', namely `y'
    In the first argument of `f', namely `(y y f)'
    In the expression: f (y y f) x

C:\maxsubseq.hs:12:14:
    The lambda expression `\ g' gmax lmax list -> ...'
    has four arguments,
    but its type `([Int] -> Int) -> [Int] -> Int' has only two
    In the second argument of `\ y f x -> f (y y f) x', namely
      `(\ g' gmax lmax list
          -> if list == [] then
                 gmax
             else
                 g' (max gmax lmax + head list) (max 0 lmax + head list) tail list)'
    In the expression:
      (\ y f x -> f (y y f) x)
        (\ y f x -> f (y y f) x)
        (\ g' gmax lmax list
           -> if list == [] then
                  gmax
              else
                  g' (max gmax lmax + head list) (max 0 lmax + head list) tail list)
    In an equation for `msss'':
        msss'
          = (\ y f x -> f (y y f) x)
              (\ y f x -> f (y y f) x)
              (\ g' gmax lmax list
                 -> if list == [] then
                        gmax
                    else
                        g' (max gmax lmax + head list) (max 0 lmax + head list) tail list)
Failed, modules loaded: none.

f (y y f) 更改为f (y f) 只是给出了

C:\maxsubseq.hs:11:29:
    Couldn't match expected type `[Int] -> Int'
                with actual type `[Int]'
    Expected type: (([Int] -> Int) -> t1 -> t0) -> t2 -> t0
      Actual type: ([Int] -> Int) -> t1 -> t0
    In the first argument of `y', namely `f'
    In the first argument of `f', namely `(y f)'
Failed, modules loaded: none.

我尝试通过仅在外部定义组合器来采用不同的方法,但这仍然不起作用,并且不能真正满足我在一个表达式中完成它的挑战。

y f = f (y f)

msss' :: [Int] -> Int
msss' = y (\g' gmax lmax list -> if list == [] 
                                 then gmax 
                                 else g' (max gmax lmax + head list) 
                                         (max 0    lmax + head list) 
                                         tail list)

你能看出我做错了什么吗?我不知所措。对构造无限类型的抱怨真的让我很生气,因为我认为 Haskell 就是那种东西。它有无限的数据结构,那么为什么会有无限类型的问题呢?我怀疑这与显示无类型 lambda 演算不一致的悖论有关。不过我不确定。如果有人能澄清一下就好了。

另外,我的印象是递归总是可以用折叠函数来表示。谁能告诉我如何仅使用折叠来做到这一点?但是,代码是单个表达式的要求仍然存在。

【问题讨论】:

  • 第二个定义有什么问题(其中y-combinator是单独定义的)?您只需要更正类型定义或在末尾添加0 0

标签: haskell recursion functional-programming y-combinator anonymous-recursion


【解决方案1】:

好吧,让我们考虑一下。这个 lambda 表达式有什么类型?

(\y f x -> f (y y f) x)

f 是函数 (a -> b) -> a -> bx 是某个值 by 是什么意思?好吧,鉴于我们刚才所说的f

(y y f) :: (a -> b)

此外,由于我们将此表达式应用于自身,我们知道y 与整个表达式具有相同的类型。这是我有点难过的部分。

所以y 是一些神奇的高阶函数。它需要两个函数作为输入。所以它有点像y :: f1 -> f2 -> f3f2 具有f 的形式,f3 具有上述结果类型。

y :: f1 -> ((a -> b) -> a -> b) -> (a -> b)

问题是……f1 是什么?嗯,它必须y 的类型相同。你看到这是如何超越 Haskell 类型系统的力量的吗?类型是根据自身定义的。

f1 = f1 -> ((a -> b) -> a -> b) -> (a -> b)

如果您想要一个独立的“单行”,那么请改用 hammar 的建议:

msss' = (\f -> let x = f x in x) 
        (\g' gmax lmax list -> case list of
            [] -> gmax
            (x:xs) -> g' (max gmax lmax + x) (max 0 lmax + x) xs
        ) 0 0

虽然恕我直言,如果 max 是允许的,那么来自 Data.Function 的 fix 也应该是允许的。除非你参加了一些仅限 Prelude 的比赛。

【讨论】:

    【解决方案2】:

    因为 Y 组合器需要无限类型,所以您需要解决方法like this one

    但我会把你的 msss 函数写成这样的单行:

    msss = fst . foldr (\x (gmax, lmax) -> let g = max (lmax + x) in (g gmax, g 0)) (0, 0)
    

    【讨论】:

    • 请注意,无限类型仅出现在 Haskell 类型系统的弱子集中,例如 System F,它既缺少递归 let 也缺少递归类型。
    【解决方案3】:

    你不能像在 Haskell 中那样定义 Y 组合子。正如您所注意到的,这会导致无限类型。幸运的是,它已经在 Data.Function 中以 fix 的形式提供,它是使用 let 绑定定义的:

    fix f = let x = f x in x
    

    【讨论】:

    • 肯定有一些方法可以在haskell中定义+使用Y组合子内联
    • @TheIronKnuckle:当然,只需内联修复的定义:(\f -> let x = f x in x)
    猜你喜欢
    • 1970-01-01
    • 2012-05-20
    • 2011-08-16
    • 2016-09-11
    • 2011-05-15
    • 1970-01-01
    • 2019-10-08
    • 2018-08-15
    • 1970-01-01
    相关资源
    最近更新 更多