使用Haskell的文件夹

时间:2018-10-21 16:55:46

标签: haskell functional-programming

我正在学习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并获取列表时的工作方式。非常感谢。

1 个答案:

答案 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. 将其添加到开头
  2. 将其添加到末尾

我认为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?好,有两种情况:

  1. 谓词在所考虑的项目上为真
  2. 该谓词对于所考虑的项为假

在第一种情况下,我们需要将我们的商品包括在列表中,因此我们可以尝试像上面的身份功能一样进行操作。

在第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 []