当前位置: 动力学知识库 > 问答 > 编程问答 >

haskell - How does this small change crash my program?

问题描述:

EDIT FOR CLARITY: I know what the 'head of empty list' error is and why it is thrown. What I don't know is why there is no error when I use 'elem' but there is if I use 'mElem'. That's the only change I make to cause the error.

If I use the 'Prelude.elem' function the program runs but with one small error. I wrote my version of 'elem' (mElem) to counter this error. I looked at the source code for 'elem' and wrote my function in a similar style. However, the program crashes due to a 'head of empty list error' resulting from the function 'getExisting'

genTupleCount :: [F.Record] -> [(String, Int)] -> [(String, Int)]

genTupleCount [] tuples = tuples

genTupleCount (x:xs) tuples | mElem (F.club x) (map fst tuples) = genTupleCount xs $ getNewTuples tuples existing

| otherwise = genTupleCount xs $ (F.club x, 1):tuples

where

existing = getExisting x tuples

getExisting :: F.Record -> [(String, Int)] -> (String, Int)

getExisting x tuples = head $ filter ((==F.club x).fst) tuples

getNewTuples :: [(String, Int)] -> (String, Int) -> [(String, Int)]

getNewTuples old e = (fst e, 1 + (snd e)):(delete e old)

mElem :: String -> [String] -> Bool

mElem _ [] = False

mElem str (x:xs) = (map toLower str) == (map toLower x) || mElem str xs

网友答案:

As Matt Fenwick said, you're assuming there is an element that satisfies your condition. I suggest using find instead:

getExisting :: F.Record -> [(String, Int)] -> Maybe (String, Int)
getExisting x = find ((==F.club x).fst)

This handles the case of there not being any such element by returning Nothing, and let's you skip mElem entirely; just check the result of getExisting to find out whether there is any such element, and if there is, what its value is.

As to why using mElem instead of elem causes your program to crash, it is because it does not verify that there is an element that satisfies the condition getExisting searches for. getExisting doesn't normalise the capitalisation as mElem does, so if nElem returns True only because of its case-folding, then the call to getExisting will occur, and it will perform head on the empty list, because filter will find no elements matching getExisting's stricter condition.

The find solution avoids this potential for error, since it only has the condition in one place.

网友答案:

Your function getExisting assumes that the result of filter ((==F.club x).fst) tuples has at least one element.

Example:

Prelude> head [3,4]
3
Prelude> head []
*** Exception: Prelude.head: empty list

It looks like switching to mElem causes an empty list to eventually be passed to head. Apparently, this doesn't happen using elem.

分享给朋友:
您可能感兴趣的文章:
随机阅读: