我正在学习Haskell,并且一直在努力解决这个问题:
使用
func :: (a -> Bool) -> [a] -> [a]
编写foldr
(取列表中的元素直到谓词为假)
这是我到目前为止所拥有的:
func :: (a -> Bool) -> [a] -> [a]
func f li = foldr f True li
,并出现以下错误:
Couldn't match expected type ‘[a]’ with actual type ‘Bool’
和
Couldn't match type ‘Bool’ with ‘Bool -> Bool’
Expected type: a -> Bool -> Bool
Actual type: a -> Bool
我有点困惑,因为我通过传递带有两个参数的函数并获得一个值来学习foldr。例如,我通过调用
使用了该函数foldr (\x -> \y -> x*y*5) 1 [1,2,3,4,5]
获取单个值,但不确定将单个参数函数传递到foldr并获取列表时的工作方式。非常感谢。
答案 0 :(得分:3)
首先让我们做一个简单的案例,然后编写一个使用foldr不执行任何操作(分解列表并创建相同列表)的函数。让我们看一下foldr
的类型签名:
foldr :: (a -> b -> b) -> b -> [a] -> [b]
我们要编写一个形式的表达式
foldr ?1 ?2 :: [a] -> [a]
现在,这告诉我们(在foldr
的签名中),我们可以将b
替换为[a]
。
?2
是我们尚未解决的事情,我们将列表的末尾替换为b = [a]
。我们实际上并没有类型a
的任何东西,所以让我们尝试一下我们能做的最愚蠢的事情:
foldr ?1 []
现在是下一个丢失的东西:我们有?1 :: a -> [a] -> [a]
。让我们为此编写一个函数。现在,我们可以使用一件事情列表和另一件事做两件合理的事情:
我认为1更合理,所以我们尝试一下:
myFunc = foldr (\x xs -> x : xs) []
现在我们可以尝试一下:
> myFunc [1,2,3,4]
[1,2,3,4]
那么这里foldr
的直觉是什么?想到的一种方法是将传递的函数而不是:
放到列表中,而另一项替换[]
以便我们得到
foldr f x [1,2,3,4]
——>
foldr f x (1:(2:(3:(4:[]))))
——>
f 1 (f 2 (f 3 (f 4 x)))
那么,如何通过仔细选择函数来做我们想做的事情(本质上是用takeWhile
实现foldr
?好,有两种情况:
在第一种情况下,我们需要将我们的商品包括在列表中,因此我们可以尝试像上面的身份功能一样进行操作。
在第2种情况下,我们不希望包含该项目,也不希望在其后包含任何内容,因此我们可以返回[]
。
假设我们的函数对谓词“小于3”做了正确的事情,这是我们可以对其进行评估的方式:
f 1 (f 2 (f 3 (f 4 x)))
--T T F F (Result of predicate)
-- what f should become:
1 : (2 : ([] ))
——>
[1,2]
因此,我们要做的就是实现f
。假设谓词称为p
。然后:
f x xs = if p x then x : xs else []
现在我们可以写
func p = foldr f [] where
f x xs = if p x then x : xs else []