在 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 -减少)。
我正在学习 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 -减少)。