【问题标题】:foldr for squareOn - HaskellsquareOn的foldr - Haskell
【发布时间】:2021-02-19 12:58:32
【问题描述】:

在我的演讲中,我们必须定义函数 squareOn 使得

foldr。 答案是

 squareOn :: (Eq a, Num a) => [a] -> a -> a        
 squareOn = foldr (\x acc y -> if y == x then x*x else acc y) id

我不明白 foldr 是如何工作的,但我是 Haskell 中 lambda 表达式的新手。 acc 是 Haskell 的任何类型的函数吗?如果有人能解释squareOn 的工作原理,那就太好了。 :)

【问题讨论】:

  • acc 只是一个变量名,如xy。它是 accumulator 的缩写,在 Haskell 中很常见(惯用语?)
  • acc 不是 any 类型;它具体是a -> a 类型之一,例如id
  • 所以,是的,acc 是这里的一个函数。

标签: function haskell lambda higher-order-functions fold


【解决方案1】:

这是foldr 的一种高级用法。通常,我们看到foldr 被用作

fun xs = foldr (\x acc -> something using x and acc) base xs

或等效

fun = foldr (\x acc -> something using x and acc) base

对应如下递归函数:

fun []     = base
fun (x:xs) = something using x and acc
   where acc = fun xs

您的情况是这种用法的一个特例,其中baseaccsomething using x and acc函数。也就是说,我们有

fun []     = \y -> base'
fun (x:xs) = \y -> something using x, acc, y
   where acc = \y -> fun xs y

回到foldr,我们得到

fun = foldr (\x acc -> \y -> something using x, acc, y) (\y -> base')

也可以写成

fun = foldr (\x acc y -> something using x, acc, y) (\y -> base')

一个令人困惑的三参数函数似乎被传递给foldr

您的具体情况,

squareOn = foldr (\x acc y -> if y == x then x*x else acc y) id

对应显式递归:

squareOn []     = id
squareOn (x:xs) = \y -> if y == x then x*x else acc y
   where acc = \y -> squareOn xs y

squareOn []     y = y
squareOn (x:xs) y = if y == x then x*x else squareOn xs y

你应该能够理解。

【讨论】:

    【解决方案2】:

    让我们在没有 lambda 的情况下定义这个函数。

    squareOn :: (Eq a, Num a) => [a] -> a -> a
    squareOn = foldr f id
      where
        f x acc = g
          where
            g y | x == y = x * x
                | otherwise = acc y
    

    现在它变成了foldr 通常的样子。它接受一个函数,该函数接受两个参数f 和一个初始值id

    当您将[2, 4] 传递给squareOn 时,它将扩展为foldr f id [2, 4],然后根据foldr 的定义扩展为f 2 (f 4 id)

    f 4 id 返回一个函数,该函数接受一个参数y,如果y4,则返回4 * 4,否则返回id y。我们称这个函数为p

    p y | 4 == y = 4 * 4
        | otherwise = id y
    

    现在,f 2 (f 4 id) 返回一个函数,该函数接受一个参数y,如果y2,则返回2 * 2,否则返回p y。当你把它命名为q时,它会是这样的。

    q y | 2 == y = 2 * 2
        | otherwise = p y
    

    例如,squareOn [2, 4] 3 等价于 q 3

    【讨论】:

      【解决方案3】:

      跳过那些明确的论点的人只会让你自己学习这些东西变得不必要地困难。这完全是肤浅的。添加由类型签名指定的显式参数,为我们提供了

      squareOn :: (Eq a, Num a) => [a] -> a -> a
      squareOn      = foldr (\x acc y -> if y == x then x*x else acc y) id
      squareOn xs   = foldr (\x acc y -> if y == x then x*x else acc y) id xs
      squareOn xs y = foldr (\x acc y -> if y == x then x*x else acc y) id xs y
      squareOn xs y = foldr g id xs y   where { g x acc y | y == x    = x*x 
                                                          | otherwise = acc y }
      squareOn xs y = (case xs of { 
          []      -> id ;
          (x:xs2) -> g x (foldr g id xs2)
                                })  y   where { g x acc y | y == x    = x*x 
                                                          | otherwise = acc y }
      

      现在我们可以看到这里的一切,而不是必须牢记在心。有下象棋,然后有下蒙眼象棋,既然能看见,何必蒙眼下呢?

      所以现在很明显,passing that y around(*) 从调用到调用 unchanged 实际上在这里没有任何目的,因为它是相同的y,并且它已经在范围内:

      squareOn xs y = (case xs of { 
          []      -> y  ;
          (x:xs2) -> g x (foldr g y  xs2)
                                })      where { g x acc   | y == x    = x*x 
                                                          | otherwise = acc   }
      

      简化为just

      squareOn xs y = foldr g y  xs     where { g x acc   | y == x    = x*x 
                                                          | otherwise = acc   }
      {- cf.
      squareOn xs y = foldr g id xs y   where { g x acc y | y == x    = x*x 
                                                          | otherwise = acc y } -}
      

      并且像您的原始代码一样,毫无意义地简短而无意义,

      squareOn      = flip (foldr g)    where { g x acc   | y == x    = x*x 
                                                          | otherwise = acc   }
      

      或者可以简化为

      squareOn xs y =  case xs of { 
          []             -> y   ;
          (x:_) | y == x -> x*x ;
          (_:xs2)        -> squareOn xs2 y }
      

      还有嵌套一元工作器的工作器/包装器,以您更清楚者为准。

      只有在没有嵌套范围的语言中才真正需要传递未更改的数量以使其在范围内,例如 Prolog。

      (*)(所以你要求的关于这种技术如何工作的完整解释实际上在链接的答案中)。

      【讨论】:

        猜你喜欢
        • 2015-01-08
        • 1970-01-01
        • 1970-01-01
        • 2022-01-24
        • 1970-01-01
        • 1970-01-01
        • 2019-07-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多