2

我在 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 到最长字符串的长度。然后将当前索引处的每个字符串的元素与彼此或美元符号进行比较。两者都可以。如果它们是其中之一,则将 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

我一直在做研究,但没有找到任何解决这个特定错误的方法。如果有人对此有所了解,我将不胜感激。

(顺便说一句,错误中的“索引”与我在函数中使用的索引不同)

4

1 回答 1

14

继续!!执行此任务表明您正在尝试将另一种语言硬塞到 Haskell 中。请允许我向您指出一个更 Haskelly 的解决方案。据我了解,这个函数执行标准的字符串相等测试,但第一个字符串允许有字符$,这是一个可以匹配任何单个字符的“通配符”。

回想一下,String在 Haskell 中只不过是一个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相等,那么我们继续检查。如果它们不相等,那么我们以 a 停止False

('$':xs) #==# (_:ys) = xs #==# ys
(x:xs) #==# (y:ys)
  | x == y    = undefined {- exercise to the reader -}
  | otherwise = False

该技术使用原始递归,而不是列表推导。如果这对您来说似乎很陌生,那么我强烈建议您查看LYAH > Recursion以了解如何思考 Haskell 方式。

于 2013-03-14T06:43:22.480 回答