如何解决squareOn 的文件夹 - Haskell
在我的讲座中,我们必须定义函数 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
是如何工作的,那就太好了。 :)
解决方法
这是 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
您的案例是这种用法的一个特例,其中 base
、acc
和 something 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,y) (\y -> base')
也可以写成
fun = foldr (\x acc y -> something using x,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
你应该能够理解。
,让我们在没有 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]
的定义扩展为 f 2 (f 4 id)
,然后是 foldr
。
f 4 id
返回一个接受一个参数 y
的函数,如果 4 * 4
是 y
,它返回 4
,否则返回 id y
。我们将此函数称为 p
。
p y | 4 == y = 4 * 4
| otherwise = id y
现在,f 2 (f 4 id)
返回一个带有一个参数 y
的函数,如果 2 * 2
是 y
,它返回 2
,否则返回 p y
.当您将其命名为 q
时,它会是这样的。
q y | 2 == y = 2 * 2
| otherwise = p y
例如,squareOn [2,4] 3
等价于 q 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(*) from call to call unchanged 实际上在这里没有任何意义,因为它是相同的 {{1 }},并且它已经在范围内:
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 }
并且要像你的原始代码一样简短而无意义,
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 }
进一步到具有嵌套一元工作器的工作器/包装器,以您更清楚为准。
只有在没有嵌套作用域的语言(如 Prolog)中才真正需要传递未更改的数量以使其在作用域内。
(*)(所以你要求的关于这种技术如何工作的完整解释实际上在链接的答案中)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。