我是Haskell的初学者。我正在尝试创建一个具有两个参数的函数:字符和字符串。 该函数应该遍历字符串并检查给定的字符是否在字符串中,然后返回表示字符串中字符位置的整数列表。
我的代码是:
tegnPose :: Char -> String -> [Int]
tegnPose c [] = []
tegnPose c (x:xs) = [if not (xs !! a == c)
then [a] ++ tegnPose c xs
else tegnPose c xs |a <- [0.. length xs - 1]]
这是一个带有列表推导的递归函数。
我得到的错误:
Uke4.hs:14:7: error:
* Couldn't match expected type `Int' with actual type `[Int]'
* In the expression: [a] ++ tegnPose c xs
In the expression:
if not (xs !! a == c) then [a] ++ tegnPose c xs else tegnPose c xs
In the expression:
[if not (xs !! a == c) then
[a] ++ tegnPose c xs
else
tegnPose c xs |
a <- [0 .. length xs - 1]]
|
14 | then [a] ++ tegnPose c xs
| ^^^^^^^^^^^^^^^^^^^^
Uke4.hs:15:7: error:
* Couldn't match expected type `Int' with actual type `[Int]'
* In the expression: tegnPose c xs
In the expression:
if not (xs !! a == c) then [a] ++ tegnPose c xs else tegnPose c xs
In the expression:
[if not (xs !! a == c) then
[a] ++ tegnPose c xs
else
tegnPose c xs |
a <- [0 .. length xs - 1]]
|
15 | else tegnPose c xs |a <- [0.. length xs - 1]]
我不明白错配是如何发生的,因为递归函数应该只是运行。
答案 0 :(得分:5)
这就是错配发生的原因。首先,请注意,返回类型[a]
列表的列表推导必须生成a
类型的元素,因此您需要以下内容进行匹配:
example :: [Int]
-- .-- the final value is "[Int]"
-- |
example = [ 2+x*y | x <- [1..10], y <- [1..5], x < y]
-- ^^^^^
-- |
-- `- therefore, this must be "Int"
在您的示例中,tegnPose
的类型签名意味着列表推导必须返回[Int]
,但表达式生成列表元素,即:
if ... then [a] ++ tegnPose c xs else tegnPose c cx
显然不按照预期的方式返回普通Int
。
第一条错误消息表明[a] ++ tegnPos c xs
的子表达式[Int]
的实际类型与应该具有类型{的整个if .. then .. else
表达式的结果的预期类型不匹配{1}}。
如果我正确理解你的问题(即,返回字符串中每个字符出现的整数位置列表,以便Int
返回tegnPose 'a' "abracadabra"
,那么你应该 < / em>使用递归或列表推导,但不能同时使用。
请注意非递归列表理解:
[0,3,5,7,10]
几乎做你想要的。缺少的只是测试条件以查看tegnPose c xs = [a | a <- [0..length xs - 1]
位置的字符是否为a
。如果你不知道在列表推导中使用“警卫”,那就去查一查。
或者,没有列表推导的递归函数:
c
也几乎做了你想做的事情,除了代替tegnPose c (x:xs) = if (x == c) then ??? : tegnPose c xs
else tegnPose c xs
tegnPose _ [] = []
代替当前位置的数字并不明显。如果您使用额外参数编写递归版本:
???
认为你可以定义:
tp n c (x:xs) = if (x == c) then n : tp (???) c xs
else tp (???) c xs
tp _ _ [] = []
然后你会更接近,只要你能弄明白tegnPose c xs = tp 0 c xs
的新值应取代n
。
更多标准的Haskell解决方案可能涉及拉链等内容:
???
和过滤器:
> zip [0..] "abracadabra"
[(0,'a'),(1,'b'),(2,'r'),...]
和地图:
> filter (\(i,c) -> c == 'a') $ zip [0..] "abracadabra"
[(0,'a'),(3,'a'),...]
或在> map fst $ filter (\(i,c) -> c == 'a') $ zip [0..] "abracadabra"
[0,3,5,7,10]
中查找符合您需要的功能:
Data.List
答案 1 :(得分:1)
只是对于某些变种,使用单个foldr
实现此功能的更简单方法可能是;
import Data.Bool (bool)
charIndices :: Char -> String -> [Int]
charIndices c = foldr (\t r -> bool r (fst t : r) (snd t == c)) [] . zip [0..]
*Main> charIndices 't' "tektronix test and measurement instruments"
[0,3,10,13,29,34,40]
说明:
foldr
的类型为Foldable t => (a -> b -> b) -> b -> t a -> b
需要三个参数;
b
a
返回单个值b
的值。
在这种特殊情况下,我们的a
类型值为Char
类型,这使得t a
成为String
类型(由于类型签名)并且类型为b
value是整数列表[Int]
。
作为第一个参数提供的函数是(\t r -> bool r (fst t : r) (snd t == c))
,如果您选中Data.bool,这非常简单。 bool
是类型为a -> a -> Bool -> a
的三元运算符,它带有三个参数。它们是负面结果,正面结果和条件。 (负面在Haskell中就像往常一样)。它检查当前字符是否等于我们的目标字符c
,如果是,则返回fst t : r
如果不是r
(r
表示结果)。最后t
是美联储元组列表的当前元组。元组列表由zip [0..] s
构成,其中s
由于部分应用而未在函数定义中显示。