在haskell中不递归地进入范围

在haskell中不递归地进入范围,haskell,currying,Haskell,Currying,我正在学习Haskell,我被这个问题困住了。我正在创建一个名为getInRange的函数,它将两个int和一个list作为参数v1、v2和iL。基本上我想遍历列表iL,得到v1和v2范围内的数字。我试图用一个高阶函数来解决这个问题,所以没有递归。我还不熟悉咖喱,并尝试在我的解决方案中使用它。我有一个助手函数“check”,可以检查给定的值x是否在v1和v2范围内。我尝试运行我的代码,但最终得到error:parse error on input'getInRange'以下是我的代码: chec

我正在学习Haskell,我被这个问题困住了。我正在创建一个名为getInRange的函数,它将两个int和一个list作为参数v1、v2和iL。基本上我想遍历列表iL,得到v1和v2范围内的数字。我试图用一个高阶函数来解决这个问题,所以没有递归。我还不熟悉咖喱,并尝试在我的解决方案中使用它。我有一个助手函数“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)

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

从列表中筛选元素的常规方法是
filter
函数,因此我的其余答案将被拆分:一个使用
filter
(这是惯用用法),另一个使用
foldr
,尽管这很不寻常(如果你想这样做,要么是为了学习,要么只是为了好玩)

过滤器
由于
filter
采用类型为
a->Bool
的过滤函数,并且您的条件返回
Bool
,因此您根本不需要
if
,因此将
check
函数更改为:

check v1 v2 z  = z > v1 && z < v2
foldr
要使用
foldr
,您需要一个类型为
a->b->b
的折叠函数,其中
a
是列表元素的类型,
b
是结果的类型。在本例中,结果是
Ord a=>a->[a]->[a]
。要使
检查
起作用,如果您的条件为真,它需要将元素附加到它正在生成的结果中,如果不为真,则不处理结果:

check v1 v2 z acc  = if (z > v1 && z < v2) then z:acc else acc
关于两种方式的最后说明
您可以使用
(x:xs)
来匹配列表。这意味着您的模式匹配是不完整的,因此您的函数是部分的,如果您使用空列表调用它,则在运行时会失败。要解决这个问题,只需在两个位置将
(x:xs)
更改为单个变量(如
xs
),或者完全删除它,因为它位于左侧和右侧的末尾(这称为eta减少)。

提示:使用
过滤器
而不是
foldr
@chepner,所以我会做一些类似的事情:让x=filter(>v1)iL,然后对v2再次使用过滤器?但同时,过滤器是一个高阶函数吗?除了修复现有的解决方案外,请始终记住,您还可以使用
列表理解
方法来生成相同的结果:
getInRange v1 v2 iL=[x | x v1,x
这本质上意味着“从列表iL中提取x,但仅当x>v1和xgetInRange :: Ord a => a -> a-> [a] -> [a] getInRange v1 v2 = filter (check v1 v2) -- see below about your (x:xs)
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
check v1 v2 z acc
  | z > v1 && z < v2 = z:acc
  | otherwise = acc