String Haskell:检查字符串中的元音
我是哈斯克尔的新手。我想写一个代码,检查字符串中是否有元音。 我提出了这个解决方案:String Haskell:检查字符串中的元音,string,haskell,String,Haskell,我是哈斯克尔的新手。我想写一个代码,检查字符串中是否有元音。 我提出了这个解决方案: n :: Int n = 0 vowel :: String -> Bool vowel a | check n = a !! n | check == 'a' || 'e' || 'i' || 'o' || 'u' || 'y' = True | check /= 'a' || 'e' || 'i' || 'o' || 'u' || 'y' = check(n
n :: Int
n = 0
vowel :: String -> Bool
vowel a
| check n = a !! n
| check == 'a' || 'e' || 'i' || 'o' || 'u' || 'y' = True
| check /= 'a' || 'e' || 'i' || 'o' || 'u' || 'y' = check(n+1)
| otherwise = False
所以基本上,我想用递归来逐字检查是否有元音。
我刚收到如下错误消息:
Couldn't match expected type `Bool' with actual type `Char'
* In the second argument of `(||)', namely 'y'
In the second argument of `(||)', namely 'u' || 'y'
In the second argument of `(||)', namely 'o' || 'u' || 'y'
|
14 | | check /= 'a' || 'e' || 'i' || 'o' || 'u' || 'y' = check(n+1)
|
问题在哪里?当前方法的问题
这段代码有很多问题:
- 您定义了一个常量
,并以某种方式期望这是n=0
检查中的默认值;事实并非如此李>
- 你首先写
,然后使用检查n=a!!n
,一个变量只有一种类型李>检查==…
- 在Haskell中,
不是赋值,而是声明。一旦声明,您就不能再更改该值李>=
- 你写
,但是check='a'|'e'|'i'|'o'|'u'|'y'
绑定的长度小于|
,所以你写了=
,因为没有布尔值,所以没有布尔值,不能将(check='a')|'e'| |'i'| | |'o'| | | | | | | | | | | y'
用作'e'
的操作数李>|
- 没有索引检查,因此即使上述问题得到解决
最终会增长到如此之大,以至于您将得到一个索引超出范围的异常李>n
- 您使用
本身并没有错,但它被认为是低效的(O(n)),并且它不是一个如此不安全的总体函数 - 你首先写
any
让我们构造一个满足需求的函数。如果我正确理解了这些要求,您需要检查元音中是否至少有一个元素
如果字符c
是'A'
,'e'
,'i'
,'o'
,'u'
,或者'y'
,那么它就是元音。所以我们可以开一张支票:
isVowel :: Char -> Bool
isVowel c = c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' || c == 'y'
但这是相当不雅的。由于String
是Char
s的列表,我们可以使用elem::Eq a=>a->[a]
函数,因此我们可以编写:
isVowel :: Char -> Bool
isVowel c = elem c "aeiouy"
vowel :: String -> Bool
vowel s = any (\c -> isVowel c) s
vowel :: String -> Bool
vowel s = any isVowel s
vowel [] = False
vowel (x:xs) = isVowel x || vowel xs
现在我们只需要检查字符串s
中是否有任何字符c
是元音,这样我们就可以写:
isVowel :: Char -> Bool
isVowel c = elem c "aeiouy"
vowel :: String -> Bool
vowel s = any (\c -> isVowel c) s
vowel :: String -> Bool
vowel s = any isVowel s
vowel [] = False
vowel (x:xs) = isVowel x || vowel xs
我们可以进一步改进这个问题。编写形式为\x->fx
的lambda表达式是无用的。相反,我们可以简单地编写f
。所以我们可以写:
isVowel :: Char -> Bool
isVowel c = elem c "aeiouy"
vowel :: String -> Bool
vowel s = any (\c -> isVowel c) s
vowel :: String -> Bool
vowel s = any isVowel s
vowel [] = False
vowel (x:xs) = isVowel x || vowel xs
我们可以在s
上应用相同的技巧:我们可以在元音
函数的头部和身体中删除它:
vowel :: String -> Bool
vowel = any isVowel
最后,我们可以使用flip
,使isVowel
函数无点:
isVowel :: Char -> Bool
isVowel = flip elem "aeiouy"
这导致:
isVowel :: Char -> Bool
isVowel = flip elem "aeiouy"
vowel :: String -> Bool
vowel = any isVowel
然后我们可以测试它。例如:
Prelude> vowel "foobar"
True
Prelude> vowel "qx"
False
Prelude> vowel "bl"
False
Prelude> vowel "bla"
True
使用递归
any::(a->Bool)->[a]->Bool
函数是一个高阶函数:它将一个函数作为输入,这会让人难以理解。我们可以为元音
函数编写自己的特殊任意
函数。我们在这里处理的是一个列表,通常在处理列表时,我们必须考虑至少两种模式:空列表<代码>[]代码>和非空列表<代码>(x:xs)。由于any
非常简单,这就足够了
如果我们处理空列表,我们知道字符串中没有元音,因此我们可以写:
isVowel :: Char -> Bool
isVowel c = elem c "aeiouy"
vowel :: String -> Bool
vowel s = any (\c -> isVowel c) s
vowel :: String -> Bool
vowel s = any isVowel s
vowel [] = False
vowel (x:xs) = isVowel x || vowel xs
如果列表非空(x:xs)
,则它有一个头部(第一个元素)x
和一个尾部(剩余元素)xs
。如果第一个元素是元音,或者任何其他元素是元音,则字符串包含元音。所以我们可以写:
isVowel :: Char -> Bool
isVowel c = elem c "aeiouy"
vowel :: String -> Bool
vowel s = any (\c -> isVowel c) s
vowel :: String -> Bool
vowel s = any isVowel s
vowel [] = False
vowel (x:xs) = isVowel x || vowel xs
如果我们把这些放在一起(连同isVowel
函数),我们可以得到:
isVowel :: Char -> Bool
isVowel c = elem c "aeiouy"
vowel [] = False
vowel (x:xs) = isVowel x || vowel xs
您的代码需要更多的关注。然而,这个具体的错误是相当直接的。运算符
|
需要两个Bool
值。但是,诸如'a'
、'b'
等值属于Char
类型,因此不能在此处使用|
您需要的是一系列布尔值来使用|
。第一个值,check=='a'
,理论上是一个Bool
,因为操作符==
可以取两个值(某些类型,包括Char
)并返回Bool
。那么,您想要的是创建一系列比较,以得到许多布尔值。因此,您必须使用=
操作符来比较检查
每个元音:
| check == 'a' || check == 'e' || check == 'i' || check == 'o' || check == 'u' || check == 'y' = True
这是开始编写代码时常见的误解。用英语可以说
如果检查等于'a'
,或'e'
,或'i'
我们试着把它直接翻译成代码。然而,在Haskell中,“or”操作符(|
)无法理解这意味着什么。您必须明确每个条件:
如果检查
等于'a'
,或检查
等于'e'
,或检查
等于'i'
一旦你解决了这个问题,你的代码会工作吗?不,还有其他问题。例如,check
没有在任何地方定义。尽管如此,每次解决一个问题,然后转到下一个问题,最终你会得到你想要的:)问题0
您已经定义了n=0
。我想你是想把它当作一种在每个递归步骤中都会增加的运行变量。这在Haskell中是不可能的–如果在顶层定义n=0
,那么n
将始终是0
要拥有这样一个“运行变量”,需要将其作为递归循环函数的参数。您似乎尝试过使用check
及其n
参数执行类似操作(这是一个完全不同的变量,它会对全局n
进行阴影处理–阴影处理很容易导致混淆,请避免)。这种本地函数的标准名称是go
,一个