Haskell 声明为空列表,但实际上哪个不为空?
Haskell Declare empty list, but which is actually not empty?
我是 Haskell 的新手,正在尝试一下。我用守卫创建了一个递归函数。请参阅下面的函数:
filterAge :: [Person] -> [String]
filterAge (x:xs)
| (x:xs) == [] = []
| (getAge x) < 30 || (getAge x) > 40 = [] ++ filterAge xs
| otherwise = [getName x] ++ filterAge xs
我有一个用 10 个人创建的数据集,我在这个方法中使用了它。当我尝试这个函数时,它给了所有合适的人,但之后它得到了一个非详尽模式错误:
["Lise","Jaap","Elle","Ebba"*** Exception: D:\...:(44,1)-(47,77): Non-exhaustive patterns in function filterAge
我发现它永远不会到达第一个守卫。所以我玩了一下,发现了一些非常奇怪的东西(在我看来):
*Main> let (x:xs) = []
*Main> (x:xs) == []
False
现在我的主要问题是:为什么 (x:xs) == []
return 是假的?
如果有人有更好的方法让我完成这个功能那就太好了,但这不是很重要。
提前致谢!
编辑
感谢 Willem Van Onsem 和 Lambda.xy.x 我的问题得到了快速解答。这导致以下功能完美运行:
filterAge :: [Person] -> [String]
filterAge [] = []
filterAge (x:xs)
| (getAge x) < 30 || (getAge x) > 40 = [] ++ filterAge xs
| otherwise = [getName x] ++ filterAge xs
但要获得最佳版本,您必须查看 Willem Van Onsem 的答案。
列表定义为:
data [] a = [] | a : [a]
所以一个列表有两个构造函数:[]
空列表和 (x:xs)
具有 一个元素 和 tail 可以存储任意数量(零个或多个)的剩余元素。
因此 (x:xs)
是一个列表 ,其中至少有一个元素:x
。 xs
可以是一个空列表(因为它的类型是 [a]
),但是 x
的类型是 a
,所以这是“head" 的列表。您的 let
语句适用于 模式匹配 ,并且由于空列表无法与 (x:xs)
匹配,它总是会失败。
另一个含义是你的第一个守卫永远不会开火。为了解决这个问题,您应该为空列表实现一个单独的案例。喜欢:
filterAge :: [Person] -> [String]
<b>filterAge [] = []</b> -- empty list case
filterAge (x:xs) -- first guard dropped
| (getAge x) < 30 || (getAge x) > 40 = [] ++ filterAge xs
| otherwise = [getName x] ++ filterAge xs
请注意,我们在第二个子句中删除了第一个守卫,因为我们知道它总是会失败,因此检查它(可能)只会花费 CPU 个周期。
还有一些地方我们可以优化:
- 我们调用了两次
getAge
,这是没有用的,我们可以使用一个where
子句来优化这个;
[] ++ somelist
只是 somelist
:在一个空列表之后附加该列表;和
[element] ++ somelist
是 element : somelist
,因为现在我们直接使用列表构造函数。
所以我们的filterAge
可以改写成:
filterAge :: [Person] -> [String]
filterAge [] = [] -- empty list case
filterAge (x:xs) | <b>age</b> < 30 || <b>age</b> > 40 = <b>filterAge xs</b>
| otherwise = <b>getName x : filterAge xs</b>
<b>where age = getAge x</b>
请注意,如果您使用 -Wincomplete-patterns
标志编译(或启动解释器)(warnings for incomplete patterns),Haskell 将自动警告您您的函数定义不完整,并且存在您尚未为其定义子句的输入模式。
为了回答您的 "main" 问题,
*Main> 让 (<strong>x</strong>:<strong>xs</strong>) = [] -- <em>( 1)</em>
表示,当需要x
或xs
的值时,匹配(x:xs)
与 []
和 使用 结果绑定。匹配将永远不会成功,任何此类尝试都会总是导致模式匹配失败错误。
那为什么不呢?
*主要> <b>(</b>x<b>:</b>xs<b>)</b> == [] -- <em>(2)</em>
现在这意味着,尝试比较 (x:xs)
和 []
。两者都是列表;比较列表涉及模式匹配顶部结构,然后递归地比较组件,成功 -- returning False
失败(实际上并没有失败)。因此,尝试了 (_:_)
和 []
之间的匹配,但失败了,导致 False
值的立即 return 作为比较结果。
注意 values 绑定到 x
和 xs
在任何时候都没有 请求;因此没有错误,因为从未触发 (1)
中的 (x:xs)
和 []
的匹配。
我是 Haskell 的新手,正在尝试一下。我用守卫创建了一个递归函数。请参阅下面的函数:
filterAge :: [Person] -> [String]
filterAge (x:xs)
| (x:xs) == [] = []
| (getAge x) < 30 || (getAge x) > 40 = [] ++ filterAge xs
| otherwise = [getName x] ++ filterAge xs
我有一个用 10 个人创建的数据集,我在这个方法中使用了它。当我尝试这个函数时,它给了所有合适的人,但之后它得到了一个非详尽模式错误:
["Lise","Jaap","Elle","Ebba"*** Exception: D:\...:(44,1)-(47,77): Non-exhaustive patterns in function filterAge
我发现它永远不会到达第一个守卫。所以我玩了一下,发现了一些非常奇怪的东西(在我看来):
*Main> let (x:xs) = []
*Main> (x:xs) == []
False
现在我的主要问题是:为什么 (x:xs) == []
return 是假的?
如果有人有更好的方法让我完成这个功能那就太好了,但这不是很重要。
提前致谢!
编辑
感谢 Willem Van Onsem 和 Lambda.xy.x 我的问题得到了快速解答。这导致以下功能完美运行:
filterAge :: [Person] -> [String]
filterAge [] = []
filterAge (x:xs)
| (getAge x) < 30 || (getAge x) > 40 = [] ++ filterAge xs
| otherwise = [getName x] ++ filterAge xs
但要获得最佳版本,您必须查看 Willem Van Onsem 的答案。
列表定义为:
data [] a = [] | a : [a]
所以一个列表有两个构造函数:[]
空列表和 (x:xs)
具有 一个元素 和 tail 可以存储任意数量(零个或多个)的剩余元素。
因此 (x:xs)
是一个列表 ,其中至少有一个元素:x
。 xs
可以是一个空列表(因为它的类型是 [a]
),但是 x
的类型是 a
,所以这是“head" 的列表。您的 let
语句适用于 模式匹配 ,并且由于空列表无法与 (x:xs)
匹配,它总是会失败。
另一个含义是你的第一个守卫永远不会开火。为了解决这个问题,您应该为空列表实现一个单独的案例。喜欢:
filterAge :: [Person] -> [String]
<b>filterAge [] = []</b> -- empty list case
filterAge (x:xs) -- first guard dropped
| (getAge x) < 30 || (getAge x) > 40 = [] ++ filterAge xs
| otherwise = [getName x] ++ filterAge xs
请注意,我们在第二个子句中删除了第一个守卫,因为我们知道它总是会失败,因此检查它(可能)只会花费 CPU 个周期。
还有一些地方我们可以优化:
- 我们调用了两次
getAge
,这是没有用的,我们可以使用一个where
子句来优化这个; [] ++ somelist
只是somelist
:在一个空列表之后附加该列表;和[element] ++ somelist
是element : somelist
,因为现在我们直接使用列表构造函数。
所以我们的filterAge
可以改写成:
filterAge :: [Person] -> [String]
filterAge [] = [] -- empty list case
filterAge (x:xs) | <b>age</b> < 30 || <b>age</b> > 40 = <b>filterAge xs</b>
| otherwise = <b>getName x : filterAge xs</b>
<b>where age = getAge x</b>
请注意,如果您使用 -Wincomplete-patterns
标志编译(或启动解释器)(warnings for incomplete patterns),Haskell 将自动警告您您的函数定义不完整,并且存在您尚未为其定义子句的输入模式。
为了回答您的 "main" 问题,
*Main> 让 (<strong>x</strong>:<strong>xs</strong>) = [] -- <em>( 1)</em>
表示,当需要x
或xs
的值时,匹配(x:xs)
与 []
和 使用 结果绑定。匹配将永远不会成功,任何此类尝试都会总是导致模式匹配失败错误。
那为什么不呢?
*主要> <b>(</b>x<b>:</b>xs<b>)</b> == [] -- <em>(2)</em>
现在这意味着,尝试比较 (x:xs)
和 []
。两者都是列表;比较列表涉及模式匹配顶部结构,然后递归地比较组件,成功 -- returning False
失败(实际上并没有失败)。因此,尝试了 (_:_)
和 []
之间的匹配,但失败了,导致 False
值的立即 return 作为比较结果。
注意 values 绑定到 x
和 xs
在任何时候都没有 请求;因此没有错误,因为从未触发 (1)
中的 (x:xs)
和 []
的匹配。