我在Haskell中有一个冗长而略显古怪的功能。
(#==#) :: String -> String -> Bool
str1 #==# str2 = (sum[ 1 | index <- [0..(max (length str1) (length str2))], (str1!!index == str2!!index || str1!!index == '$')] == (max (length str1) (length str2)))
简而言之,此函数检查两个字符串是否相同,如果它们有一个或多个'$',则认为它们相同 [长版本:为了节省您解密它的时间,它需要两个字符串,列表解析中的索引变量从0到最长字符串的长度。然后将当前索引处的每个String的元素与彼此或美元符号进行比较。两者都可以。如果它们是其中之一,则将1添加到新列表中,如果此新列表的总和等于长度,则该单词是匹配的。
当我尝试运行时,我得到一个特殊的错误:
*Practice> let totals = (sum[ 1 | index <- [1..(max (length str1) (length str2))], (str1!!index == str2!!index || str1!!index == '$')] == (max (length str1) (length str2)))
*Practice> totals
*** Exception: Prelude.(!!): index too large
我一直在做研究,但没有找到任何解决这个特殊错误的方法。如果有人知道任何事情,我会非常感激。
(顺便说一句,错误中的“索引”与我在函数中使用的索引不同)
答案 0 :(得分:12)
回到!!
执行此任务表明您正试图将另一种语言用于Haskell。请允许我指出更多Haskelly解决方案。据我所知,这个函数执行标准字符串相等测试,但第一个字符串允许有一个字符$
,这是一个可以匹配任何单个字符的“通配符”。
回想一下,Haskell中的String
只不过是Char
的列表。因此,我们可以在两个列表构造函数上进行模式匹配:空列表和非空列表。对两个列表的两种可能性进行匹配为我们提供了四种可能的组合:
(#==#) :: String -> String -> Bool
[] #==# [] = ???
(x:xs) #==# [] = ???
[] #==# (y:ys) = ???
(x:xs) #==# (y:ys) = ???
考虑两个列表是否为空。他们匹配吗?让我们说,确实,他们这样做。这是一个重要的基本案例选择,但是现在我只是诉诸于将空字符串放入原始代码的事实应该产生True
。
[] #==# [] = True
让我们看看中间的两种情况,其中一个列表为空,但另一个列表不是。
(x:xs) #==# [] = ???
[] #==# (y:ys) = ???
您从未指定长度不均匀的列表会发生什么。但是,为了保留似乎是原始算法的内容,如果第一个列表填充了$
,那么我们称之为好,否则,它不匹配。因此,我们将检查左侧列表的第一个元素,如果它是$
,那么我们将继续检查列表的其余部分。
('$':xs) #==# [] = xs #==# []
(x:xs) #==# [] = False
[] #==# (_:_) = False
让我们看看有趣的案例,两者都是非空的。
(x:xs) #==# (y:ys) = ???
如果左手第一个字符是$
,那么我们忽略任何正确的字符,并继续检查。如果绑定到x
和y
的字符相等,那么我们会继续检查。如果它们不相等,那么我们停在False
。
('$':xs) #==# (_:ys) = xs #==# ys
(x:xs) #==# (y:ys)
| x == y = undefined {- exercise to the reader -}
| otherwise = False
这种技术使用原始递归,而不是列表推导。如果这对您来说似乎很陌生,那么我强烈建议您查看LYAH > Recursion以了解Haskell方式的好介绍。