给出以下功能:
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
答案 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 z
,finalStep $ foldl f z xs
和f
中的任何一个z
和xs
都是相同的。