我试图手动评估,fc [f1, f2] (\x -> 2) 3
但我不知道如何匹配三个参数:[f1,f2]、(\x -> 2) 和 3 与函数定义,有什么帮助吗?
函数定义:
fc xss = \f -> let ope x y = x . f . y in foldr1 ope xss
f1, f2 :: Int -> Bool
f1 x = (x `mod` 3) == 0
f2 x = (x `mod` 5) == 0
谢谢,
塞巴斯蒂安。
我试图手动评估,fc [f1, f2] (\x -> 2) 3
但我不知道如何匹配三个参数:[f1,f2]、(\x -> 2) 和 3 与函数定义,有什么帮助吗?
函数定义:
fc xss = \f -> let ope x y = x . f . y in foldr1 ope xss
f1, f2 :: Int -> Bool
f1 x = (x `mod` 3) == 0
f2 x = (x `mod` 5) == 0
谢谢,
塞巴斯蒂安。
好吧,首先做一些η -expansion
fc :: [a->b] -> (b->a) -> a->b
fc xss f = let ope x y = x . f . y in \q -> foldr1 ope xss q
fc xss f q = let ope x y = x . f . y in foldr1 ope xss q
然后也许内联let
fc xss f q = foldr1 (\x y -> x . f . y) xss q
你可以[f1, f2]
写成(==0).(`mod`3) : (==0).(`mod`5) : []
.
现在应该很容易了,记住右折叠只是用:
折叠函数替换所有。
请记住,Haskell 中的所有函数都是针对单个结果的单个参数的函数。
像这样的表达式func arg1 arg2 arg3
可能看起来像一个应用于 3 个参数的函数,但实际上((func arg1) arg2) arg3)
,它func arg1
会产生一个新函数,该函数被应用于arg2
获得第三个函数,该函数最终应用于arg3
.
like 定义func arg1 arg2 arg3 = ...
是等价定义的语法糖func arg1 = \arg2 -> (\arg3 -> ... )
,我们已经明确地写出了沿途产生的中间函数。
fc [f1, f2] (\x -> 2) 3
应用fc
到[f1, f2]
,然后应用其结果也是如此。很清楚如何应用于fc
一个参数(因为它的定义只有一个参数),所以让我们做第一步,看看我们得到了什么。
fc [f1, f2] = \f -> let ope x y = x . f . y in foldr1 ope [f1, f2]
这给了我们一个 lambda 函数,这是可以应用的。将其代入原始表达式可以得到:
(\f -> let ope x y = x . f . y in foldr1 ope [f1, f2]) (\x -> 2) 3
现在我们再次将函数 ( \f -> ...
) 应用于一个参数 ( \x -> 2
)。结果再次应用(到3
),但我们稍后会担心。
(\f -> let ope x y = x . f . y in foldr1 ope [f1, f2]) (\x -> 2)
= let ope x y = x . (\x -> 2) . y in foldr1 ope [f1, f2]
所以这给了我们表达式,以及来自;foldr1 ope [f1, f2]
的辅助定义。如果我们愿意,我们可以替换它,但我会让这个名字作为一个替身。替换回来(记住我们已经定义了):ope
let
ope
ope
(foldr1 操作 [f1, f2]) 3
foldr
等效于以下内容:
foldr1 f [x] = x
foldr1 f (x:xs) = f x (foldr1 f xs)
所以我们可以扩展foldr1
表达式,记住这[f1, f2]
只是另一种写法f1:[f2]
:
foldr1 ope (f1:[f2]) = ope f1 (foldr1 ope [f2])
= ope f1 f2
将其替换回给我们:
ope f1 f2 3
现在让我们使用我们预留的 ope 的定义:(ope x y = x . (\x -> 2) . y
同样,我们可以在前面替换它,但这只是意味着复制我们ope
上面写的任何地方的定义)。记住这ope
实际上是单个参数的函数,即使我们被允许定义它看起来像它有两个参数一样方便,让我们一次应用一个参数:
ope f1 = \y -> f1 . (\x -> 2) . y
所以我们现在有:
(\y -> f1 . (\x -> 2) . y) f2 3
应用 lambda 给我们:
(f1 . (\x -> 2) . f2) 3
最后我们发现是组合 "pipeline" 的一个参数。我将在此停止,但您可以使用 的定义来进一步扩展它并找出传递给的最终函数。3
f1 . (\x -> 2) . f2
.
3
因此,原始表达式中的 3 个值[f1, f2]
、(\x -> 2)
和3
不需要直接匹配为 定义的参数fc
,因为它们根本不是它的所有参数。[f1, f2]
是 的参数fc
,(\x -> 2)
是一个由 简单计算的参数fc [f1, f2]
(因此,如果需要,您可以将其视为“第二个参数” fc
;重写等式很容易fc
,使其直接显示为参数) . 3
另一方面,它不是函数的参数,它接近于直接写在源代码中,它最终被传递给由表达式计算fc [f1, f2] (\x -> 2)
的函数。