使用文件夹制作过滤器

时间:2019-07-19 16:48:39

标签: haskell fold

我正在尝试使用filter来制作foldr。我有一个解决方案,但不知道为什么我的版本不起作用。

foldr的工作方式如下:

>>>foldr op z (xs:x)
x op z

然后重复,对吗?当x是列表中的最后一个元素时。

现在,我有

myFilter pred = foldr op z
  where
    z = []
    op x xs = if pred x then x

这不起作用,给出了parse error (possibly incorrect indentation or mismatched brackets)错误。

该操作仅将x赋予pred,如果为true,则返回x,否则将其跳过。 foldr的工作方式是在所传递的xs列表上不断循环。那为什么我需要做

 op x xs = if pred x then x : xs else xs

并告诉它继续xs,即使foldr已经继续执行了吗?

3 个答案:

答案 0 :(得分:5)

foldr :: (a -> b -> b) -> b -> [a] -> b遍历a的输入列表,并操纵b(代表循环的状态)。 op的工作是从输入列表中获取a,即当前状态b,然后计算一个新状态。 foldr确保列表中的每个项目都调用op

对于您的filterb也是a的列表。因此,您的代码正在讨论a的两个不同列表-输入列表和输出列表。您的op必须决定我是否应该将此x放在输出列表中?

为澄清起见,这是您的代码,其中的变量命名更具暗示性。

filter :: (a -> Bool) -> [a] -> [a]
filter pred = foldr addToOutputIfNecessary initialOutput
  where
    initialOutput = []
    addToOuputIfNecessary itemFromInput currentOutput =
      if pred itemFromInput
      then itemFromInput:currentOutput
      else currentOutput

因此,当我们从currentOutput返回addToOutputIfNecessary时,并不是要告诉foldr继续。 (foldr本身将始终继续到下一个输入元素-您无需告诉它继续。)这只是说循环的状态并没有改变此迭代;我们决定不向输出列表添加项目。

希望这会有所帮助!如果您不了解任何内容,请在评论中告诉我。

答案 1 :(得分:5)

foldr封装了将列表中的元素组合在一起以及折叠其余列表的结果所涉及的递归。 xs不是您输入内容的结尾;这是折叠输入尾部的结果。

您可以通过重点关注op要执行的操作来强调这一点,并通过重构

来不强调它的操作。
op x xs = if pred x then x : xs else xs

-- (x:) xs == x:xs
-- id xs == xs
op x xs = (if pred x then (x:) else id) xs

可以被eta化为

op x = if pred x then (x:) else id

换句话说,给定列表的第一个元素,您将如何处理:将x附加到递归结果中,还是按原样返回该结果?

答案 2 :(得分:3)

让我们看看列表的foldr的类型:

foldr :: (a -> b -> b) -> b -> [a] -> b 

现在您正在像这样使用它:

foldr op z

foldr的类型签名中可以看到,函数op必须返回类型为b的东西,该类型与z相同。由于您执行z = [],因此它必须是一个列表。因此op x xs的结果必须是列表。

此外,请记住,Haskell中的if...else表达式。这意味着无论条件是对还是错,它都必须求值到某个值。在其他语言中,else是可选的,因为if声明。在Haskell中,else 不是可选的

  

那我为什么需要做

op x xs = if pred x then x : xs else xs
     

并告诉它继续使用xs,即使foldr已经执行了该操作?

一个原因是op必须始终返回一个值。 foldr将使用此值继续进行计算。没有它,foldr将无法继续,实际上您的代码甚至无法如您所见进行编译。