描述给定文件夹功能的前几个评估步骤

时间:2019-05-30 01:46:17

标签: haskell functional-programming

给出以下功能:

mystery a b c xs = 
  foldr (\x rec a b -> rec x (b + c)) (\a b -> a + b - c) xs a b

我知道函数的大致功能,但是我很难真正理解中间步骤。我选择了以下示例:

mystery 1 2 3 [1, 2, 3]

尤其是rec的使用使我很难受。我认为最后一步之一是这样的:

(\a b -> 3 + (b + 3 + 3 + 3) - 3) [] 1 2

因此,输出为11。有人可以描述执行的前几个步骤吗?之后会发生什么:

foldr (\x rec a b -> rec x (b + 3)) (\a b -> a + b - 3) [1, 2, 3] 1 2

3 个答案:

答案 0 :(得分:4)

这里的关键是文件夹操作正在构造一个函数,然后将其应用于a和b;我们可以通过添加括号来澄清它:

mystery a b c xs = 
(foldr (\x rec a b -> rec x (b + c)) (\a b -> a + b - c) xs) a b

如果xs列表为空,则仅获得初始函数\a b -> a + b - c,然后将其应用于a和b。

如果它不为空,那么它将对该函数进行成功的转换(在每次迭代中,“ rec”是前一个函数,用于构造一个新函数)。

为说明起见,让我们手动运行mystery 1 2 3 [1, 2, 3]的文件夹; 最初,我们有:

foldr (\x rec a b -> rec x (b + 3)) (\a b -> a + b - 3) [1,2,3]

将等式应用于文件夹:

foldr f z []     = z 
foldr f z (x:xs) = f x (foldr f z xs)

将表达式简化为:

(\rec a b -> rec 1 (b + 3)) (foldr (\x rec a b -> rec x (b + 3)) (\a b -> a + b - 3) [2,3])

重复列表中的下一个值,我们得到:

(\rec a b -> rec 1 (b + 3)) (\rec a b -> rec 2 (b + 3)) (foldr (\x rec a b -> rec x (b + 3)) (\a b -> a + b - 3) [3])

然后,对于最后一个:

(\rec a b -> rec 1 (b + 3)) (\rec a b -> rec 2 (b + 3)) (\rec a b -> rec 3 (b + 3)) (\a b -> a + b - 3)

我们需要组合这些功能,以创建最终功能-用上一个功能替换“ rec”:

(\rec a b -> rec 1 (b + 3)) (\rec a b -> rec 2 (b + 3)) (\rec a b -> rec 3 (b + 3)) (\a b -> a + b - 3)
=> (\rec a b -> rec 1 (b + 3)) (\rec a b -> rec 2 (b + 3)) (\a b -> (\a b -> a + b - 3) 3 (b + 3))
=> (\rec a b -> rec 1 (b + 3)) (\rec a b -> rec 2 (b + 3)) (\a b -> 3 + (b + 3) - 3))
=> (\rec a b -> rec 1 (b + 3)) (\a b -> (\a b -> 3 + (b + 3) - 3)) 2 (b + 3))
=> (\rec a b -> rec 1 (b + 3)) (\a b -> 3 + ((b + 3) + 3) - 3))
=> \a b -> (\a b -> 3 + ((b + 3) + 3) - 3)) 1 (b + 3)
=> \a b -> 3 + (((b + 3) + 3) + 3) - 3)
=> \a b -> b + 9

然后,我们将\a b -> b + 9应用于原始的“ a”和“ b”(分别为1和2),得到2 + 9 = 11

答案 1 :(得分:3)

用定义foldr代替,该函数表明自己是

mystery a b c xs = 
 = foldr (\x rec a b -> rec x (b + c)) (\a b -> a + b - c) xs a b
 = let { g x r   a b =  r   x (b + c); z a b =  a + b - c } in 
     foldr g z xs a b
     =>  foldr g z [] a b = z a b            = a  + b       - c
     =>  foldr g z [x1,x2,...,xn] a  b
          = g x1 (foldr g z [x2,...,xn]) a b   -- g x r a b = r x (b+c)
          = foldr g z [x2,...,xn] x1 (b+c)
          = foldr g z [x3,...,xn] x2 (b+c*2)
          = foldr g z [         ] xn (b+c*n) = xn + b + c*n - c
 = last (a:xs) + b + c * (length xs - 1)

使用短名称命名这两个lambda函数,可以更轻松地直观地处理表达式。

答案 2 :(得分:0)

foldr的折叠功能使用两个以上的参数时,通常实际上是伪装的foldl。让我们看看这里是否正确。

mystery a b c xs = foldr (\x rec a b -> rec x (b + c)) (\a b -> a + b - c) xs a b

取消折叠函数的两个“额外”参数的使用:

mystery a b c xs = foldr (\x rec (a,b) -> rec (x,b + c)) (\(a,b) -> a + b - c) xs (a,b)

从折叠函数中提取函数f,从零例中提取finalStep

mystery a b c xs = foldr (\x rec z -> rec (f z x)) finalStep xs (a,b)
  where
    f (a,b) x = (x,b + c)
    finalStep (a,b) = a + b - c

用显式递归替换foldr

mystery a b c xs = go xs (a,b)
  where
    go [] = finalStep
    go (x:xs) = \z -> go xs (f z x)
    f (a,b) x = (x,b + c)
    finalStep (a,b) = a + b - c

将呼叫移至finalStep之外的go

mystery a b c xs = finalStep $ go xs (a,b)
  where
    go [] = id
    go (x:xs) = \z -> go xs (f z x)
    f (a,b) x = (x,b + c)
    finalStep (a,b) = a + b - c

展开go并反转参数的顺序:

mystery a b c xs = finalStep $ go (a,b) xs
  where
    go z [] = z
    go z (x:xs) = go (f z x) xs
    f (a,b) x = (x,b + c)
    finalStep (a,b) = a + b - c

现在,go正是foldl f的定义,因此将其替换为:

mystery a b c xs = finalStep $ foldl f (a,b) xs
  where
    f (a,b) x = (x,b + c)
    finalStep (a,b) = a + b - c

现在,我们有一个非常简单的折叠操作,可以轻松完成。上文的主要结论是,foldr (\x rec -> rec . f x) finalStep xs zfinalStep $ foldl f z xsf中的任何一个zxs都是相同的。