我想学习如何主要使用head和tail创建函数。我需要知道的是元素的协议或表示,如何识别元素以及如何定义函数。例如,我正在练习如何编写一个简单的函数来查找两个字符串的公共元素。像这样:
commonElt :: (Eq a)=> [a]->[a]-> bool
commonElt [] _ = true
commonElt _ [] = false
commonElt s1 s2 |if elm (head s1)s2 == true then show head s1 && commonElt(tail s1)s2
|otherwise = commonElt(tail s1) s2
其中s1
和s2
是字符串。但是我无法加载模块,因为我收到以下错误消息:“解析错误。可能是错误的识别。”
答案 0 :(得分:4)
修改:julia
:不可避免的是,在写作时hammar
出现了同样的答案,但也许有一些有用的区别。
julia
,正如larsmans
所说,一个问题是你没有大写类型及其构造函数的名称。 Bool的定义是:
data Bool = True | False
双方的话都是大写的; Bool
是类型的名称,True
和False
是我们在分割案例时匹配的构造函数。相比之下,如果我们定义的新术语是函数和其他值,我们使用小写。假设我想定义not :: Bool -> Bool
,有两种情况:
not True = False
not False = True
or :: Bool -> Bool -> Bool
有四种情况,但我们可以缩短内容。
or False False = False
or _ _ = True
所以在这里,not
和or
都以小写字母引入。现在要解决您的问题,首先要注意的是类型签名和前两种情况,
commonElt :: (Eq a) => [a] -> [a] -> Bool
commonElt [] _ = True
commonElt _ [] = False
并不真正符合您的规范,即找到共同的元素。那将有签名
commonElts :: (Eq a) => [a] -> [a] -> [a]
如果我们修复前两行以符合我们的抱负
commonElts [] _ = []
commonElts _ [] = []
然后,根据您的第三个案例的想法,如果我们注意到在相关案例中,任何一个术语都不会被构造为head
,那么我们可以免费使用tail
和[]
。而是:
,所以情况就是这样说:
commonElts (s:ss) (t:ts)
您的想法是检查s
是否在第二个列表中,然后将其包含在使用commonElts
在剩余位ss
上构建的列表中。有几种方法可以解决这个问题,我将按照您的示例使用if
。请注意,Haskell的if...then..
始终为if ... then --- else ---
;第一个空白是布尔测试,其他两个空格可以是任何类型,只要它是相同的,因此我们通常将它们排成一行:
commonElts (s:ss) (t:ts) =
if elem s (t:ts) == True then s : commonElts ss (t:ts)
else commonElts ss (t:ts)
请注意。但是,elem s (t:ts)
已经是Bool,无论是真还是假,所以我们不需要添加== True
。此外,我们实际上并未将第二个列表划分为t
和ts
,因此我们可以像您一样将其称为ts
或s2
。因此,完整的定义是:
commonElts [] _ = []
commonElts _ [] = []
commonElts (s:s1) s2 =
if elem s s2 then s : commonElts s1 s2
else commonElts s1 s2
如果一个元素在第一个列表中出现多次,它将在“common element”列表中出现多次;我们可以通过进一步区分案例来解决这个问题。
编辑:请注意,可以使用'list comprehension'定义一个非常相似的函数:
common xs ys = [ x | x <- xs , y <- ys, x == y]
答案 1 :(得分:3)
Haskell中的缩进很重要。您应该对齐commonElt
:
commonElt [] _ = true
commonElt _ [] = false
commonElt s1 s2 ...
然后意识到布尔值被称为True
和False
; Haskell区分大小写。
答案 2 :(得分:3)
你在这里遇到很多错误,并不清楚你的功能究竟应该做些什么,但这是尝试对它有所了解:
首先,定义commonElt
的行不应缩进。此外,Bool
,True
和False
使用前导大写字母编写,因为它们是构造函数(Bool
是类型构造函数,True
和{{ 1}}是数据构造函数)。
其次,你不要在这样的警卫中使用False
。只需在if
和|
之间自行编写条件。此外,=
是不必要的,我认为您打算使用== True
,而不是elem
。
其余的,虽然在语法上有效,但不会检查或有意义。 elm
返回show
,String
不能与(&&)
一起使用,因为它仅适用于布尔值(类型为Bool -> Bool -> Bool
)。
commonElt :: (Eq a) => [a] -> [a] -> Bool
commonElt [] _ = True
commonElt _ [] = False
commonElt s1 s2 | elem (head s1) s2 = show (head s1) && commonElt (tail s1) s2
| otherwise = commonElt (tail s1) s2
此时我不清楚你要做什么,但正如你在问题陈述中所说的那样,你试图找到两个列表的共同元素,我想你想要这样的东西:
commonElt :: (Eq a) => [a] -> [a] -> [a]
commonElt [] _ = []
commonElt s1 s2 | elem (head s1) s2 = head s1 : commonElt (tail s1) s2
| otherwise = commonElt (tail s1) s2
我们不是返回Bool
,而是使用s1
运算符从s2
中收集(:)
中的元素列表,该运算符构建了一个列表。头元素和尾部列表。
当然,使用更高阶函数可以更简洁地写出来:
commonElt s1 s2 = filter (`elem` s2) s1
此功能已存在于Data.List
模块中,名为intersect
。