在 haskell 中无需递归进入范围

Get in range without recursion in haskell

我正在学习 Haskell 并且遇到了这个问题。我正在创建一个名为 getInRange 的函数,它接受两个整数和一个列表作为参数 v1、v2 和 iL。基本上我想遍历列表 iL,并获取 v1 和 v2 范围内的数字。我正在尝试使用高阶函数来解决这个问题,所以没有递归。我也是 currying 的新手,并尝试在我的解决方案中尝试使用它。我有一个辅助函数 'check' 可以检查给定值 x 是否在 v1 和 v2 的范围内。我尝试 运行 我的代码,但我最终得到 error: parse error on input 'getInRange' 这是我的代码:

check v1 v2 z  = if (z > v1 && z < v2) then z

--getInRange
getInRange :: Ord a => a -> a-> [a] -> [a]
getInRange v1 v2 (x:xs) = foldr (check v1 v2) [] (x:xs)

以输入输出为例,

getInrange 3 5 [1,2,3,4,5,6,7] 
= [4]

您收到语法错误,因为您的 if 没有 else。在 Haskell 中,这始终是强制性的。

从列表中过滤元素的正常方法是 filter 函数,因此我的答案的其余部分将被拆分:一个使用 filter(惯用的),一个使用使用 foldr,尽管这很不寻常(如果您想那样做,无论是为了学习还是为了好玩)。

filter

由于 filter 采用 a -> Bool 类型的过滤函数,并且您的条件 returns 为 Bool,您不需要 if所有,所以把你的 check 函数改成这个:

check v1 v2 z  = z > v1 && z < v2

然后将 getInRange 函数更改为使用 filter:

getInRange :: Ord a => a -> a-> [a] -> [a]
getInRange v1 v2 = filter (check v1 v2) -- see below about your (x:xs)

foldr

要使用foldr,你需要一个a -> b -> b类型的折叠函数,其中a是列表元素的类型,b是结果。在这种情况下,结果为 Ord a => a -> [a] -> [a]。要使 check 工作,它需要将元素附加到它正在构建的结果中(如果您的条件为真),如果条件不成立则保留结果:

check v1 v2 z acc  = if (z > v1 && z < v2) then z:acc else acc

或者,清理一下:

check v1 v2 z acc =
  if z > v1 && z < v2
    then z:acc
    else acc

如果需要,您可以使用守卫而不是 if 语句来表达条件,有些人认为后者更惯用:

check v1 v2 z acc
  | z > v1 && z < v2 = z:acc
  | otherwise = acc

关于两种方式的最后说明

您使用 (x:xs) 来匹配列表。这意味着您的模式匹配不完整,因此您的函数是部分的,如果您使用空列表调用它,它将在运行时失败。要解决这个问题,只需在两个地方将 (x:xs) 更改为单个变量(如 xs),或者完全删除它,因为它位于左侧和右侧的末尾(这称为 eta -减少)。