我正在考试为即将到来的考试而考试,在完成几个问题后,我遇到了一个我无法解决的问题。
它需要一个函数,它将接受一个String(或[Char])并返回一个字符串中英文单词数量的Int。它表示isWord是一个假设函数,它接受一个String并返回一个布尔值,具体取决于该单词是true还是false。 单词必须连续,从左到右。给出的例子是“catalogre”。所以“cat”,“at”,“catalog”,“ogre”和“log”,函数应该返回5。
wordsInString :: [Char] -> Int
wordsInString [] = 0
wordsInString x
| isWord (take 1 x)
| isWord (take 2 x)
保险杠只是显示我的想法,显然它不会起作用。
这就是我的开始,我想我可以使用take
函数并一次递增每个字母,然后将起始字母向下移动到[]
,但我不是'确定如何正确实现该递归。如果有人有任何想法或可以告诉我一个方法,那就太好了。
答案 0 :(得分:7)
如果您知道如何区分单词和非单词,则可以使用inits
和tails
获取所有可能候选人的列表:
> :m +Data.List
> concatMap inits $ tails "catalogre"
["","c","ca","cat","cata","catal","catalo","catalog","catalogr","catalogre","","a","at","ata","atal","atalo","atalog","atalogr","atalogre","","t","ta","tal","talo","talog","talogr","talogre","","a","al","alo","alog","alogr","alogre","","l","lo","log","logr","logre","","o","og","ogr","ogre","","g","gr","gre","","r","re","","e",""]
答案 1 :(得分:4)
该问题陈述有点模糊。我将做一些未明确说明的假设 - 一个单词可以是另一个单词的前缀,并且每次重复的单词都会计数。
然后,为了解决这样的问题,将其分解为部分。你已经完成了一些这方面的工作,但你似乎没有跟进代码。 Haskell的一个强大功能是您的代码结构通常会遵循您的思想结构。
因此,您已经明确决定要生成所有适当的子字符串进行测试,然后计算结果。让我们首先将其放入代码中。
wordCount :: String -> Int
wordCount = length . findWords
findWords :: String -> [String]
findWords = filter isWord . makeSubstrings
makeSubstrings :: String -> [String]
makeSubstrings xs = undefined -- hmm, this isn't clear yet
确定。这是一个起点。它归结为问题的核心。你打算如何测试所有候选子串呢?
嗯,你的问题已经显示了必要的想法。只需将它们分解成足够小的碎片就可以看到它们是如何做到的。你提到想要从字符串中的每个起始位置做一些事情。那么如何编写一个函数,从每个位置开始返回字符串,并结束?这似乎是合乎逻辑的第一步。
-- for the input "foo", this should return the list ["foo", "oo", "o", ""]
tails :: String -> [String]
tails = undefined -- I'll leave this one up to you
名称的选择不是任意的。有一个功能在Data.List
中已经完成了这个功能,但你应该自己实现它,只是为了看看它是如何完成的。
但是你清楚地看到你需要查看那些的所有前缀,你的想法是为了拍摄。因此,编写另一个函数来生成字符串的所有前缀。这也存在于Data.List
inits
中,但请再次尝试自己编写。
-- for the input "foo", this should return the list ["", "f", "fo", "foo"]
inits :: String -> [String]
inits = undefined - again, this is up to you
而且,对于map
和concat
,这些与实现makeSubstrings
所需的部分相加,正如其他答案所示。希望我能真正传达一种如何推理必要步骤的感觉,以及如何使用这些步骤来构建代码。
答案 2 :(得分:2)
您正在从Data.List中寻找 subsequences
函数。
阅读the libraries that come with GHC,特别是基础,这是一个好主意。即使您不允许在考试中使用这些功能,阅读源代码仍然有用,有时也很有启发性(按照类型签名右侧的“源”链接)。
编辑:评论是正确的,Matvey的答案也是如此。你可以不接受我的回答,而是接受Matvey的。
答案 3 :(得分:1)
allWordsInString :: [Char] -> [[Char]]
allWordsInString = filter isWord . concat . map tails . inits
-- ^^^^^^^^^^^^^^^^^^ or, concatMap tails
wordsInString :: [Char] -> Int
wordsInString = length . allWordsInString
我会建议这样的事情,因为知道你给定字符串中的英文单词可能会很有趣。
(.)
是功能组合。 concat :: [[a]] -> [a]
展开列表,例如concat [[1,2], [], [3] == [1,2,3]
。 inits
返回给定列表的所有可能的初始前缀tails
,后缀相同。 filter :: (a -> Bool) -> [a] -> [a]
最终获取谓词,列表,并返回仅包含满足谓词的元素的列表。
答案 4 :(得分:0)
这是另一种解决方案,它不使用任何花哨的Haskell功能,而不是连接列表,计算列表的长度,获取列表的尾部 - 以及递归。
这个想法是这样的:
首先编写一个函数candidatesWithLength :: Int -> String -> [String]
,它给出一个项长度和一些字符串,然后生成一个包含该长度的所有项的列表,这样它的行为如下:
> candidatesWithLength 3 "Foo"
["Foo"]
> candidatesWithLength 2 "Foo"
["Fo", "oo"]
> candidatesWithLength 1 "Foo"
["F", "o", "o"]
然后,使用上面的candidatesWithLength
函数,编写一个函数candidates :: String -> [String]
,它产生给定字符串的所有“候选”(潜在的单词)。该函数只是构建一个长列表,所有长度为1的候选者插入长度为2的候选者,加上长度为3的候选者,依此类推。它的行为如下:
> candidates "Foo"
["Foo", "Fo", "oo", "F, "o", "o"]
如果你有这个,你可以使用返回列表中的现有filter
函数,以便跳过给定isWord
函数产生错误的所有内容,如下所示:< / p>
> filter isWord (candidates "catalogre")
["catalog", "ogre", "cat", "log", "at"]
以下是两种方法candidatesWithLength
和candidates
的实现,它们没有使用太多花哨的功能:
candidatesWithLength :: Int -> String -> [String]
candidatesWithLength len s
| len > (length s) = []
| otherwise = go s (length s - len + 1)
where go _ 0 = []
go s' movesLeft = take len s' : go (tail s') (movesLeft - 1)
candidates :: String -> [String]
candidates s = go (length s)
where go 0 = []
go itemLength = candidatesWithLength itemLength s ++ go (itemLength - 1)