我在构建针对以下问题的算法时遇到了麻烦:给定一本教科书(this one),找到每个部分的每个第一段和最后一段。
通过传递newline参数,使用split
,groupify
或filter
在Haskell中轻松获取每个段落,并且工作正常。然而,其余的对我来说很复杂(我只熟悉OOP)。如果我们将标题作为基本情况,则文本中将有两个标题字符串:一个在索引部分,一个在文本本身。程序如何区分它们?此外,代码如何知道两段之间是否有两个空行?
这是我尝试解决问题;
通过将文本拆分为段落
搜索数组中标题的索引,找到每个标题的第二次出现以及该索引的名称
将paragraph_array [index + 1]作为第一个元素
将paragraph_array [index-1]作为上一节的最后一个元素。
我很困惑。任何帮助都将非常感激。
更新:某个部分类似于每个标题的一部分。例如,微积分书可以包含12章,每章可以包含8个章节。我们需要考虑那本书只有一章,它有4-5个部分。 标题可以是INTEGRAL,LIMIT等。
答案 0 :(得分:0)
以下是我将如何处理此问题的草图。
我假设你有一些功能isTitle :: String -> Bool
可以
确定某个字符串是否是标题并且您是否有文本
分为段落和标题列表(即[String]
类型)。
{-# LANGUAGE RecordWildCards #-}
module FindParagraphs where
import Data.Maybe
我从一个数据类型开始,将两个元素的光标放入一个列表中, 这种数据类型通常称为Zipper。
data TwoZip a =
TwoZip { front :: [a]
, first :: a
, second :: a
, back :: [a]
}
deriving (Eq, Show)
-- make a zipper from a list
mkZip :: [a] -> Maybe (TwoZip a)
mkZip (first:second:back) = Just (TwoZip [] first second back)
mkZip _ = Nothing
-- advance the zipper
advance :: TwoZip a -> Maybe (TwoZip a)
advance TwoZip{..}
| (x:xs) <- back = Just (TwoZip (first:front) second x xs)
advance _ = Nothing
-- and rewind our zipper (not needed in this case but nice for
-- completeness sake)
rewind :: TwoZip a -> Maybe (TwoZip a)
rewind TwoZip{..}
| (x:xs) <- front = Just (TwoZip xs x first (second:back))
rewind _ = Nothing
要找到所有第一段,我们会迭代段落并查看 在拉链的第一个元素,如果它是标题,我们知道 拉链中的下一个元素是第一段,然后我们前进!
findFirsts
:: (String -> Bool) -- ^ is collection of words a title
-> [String] -- ^ Titles and paragraphs
-> [String] -- ^ last and first paragraphs
findFirsts isTitle paragraphs = reverse (go (fromJust (mkZip paragraphs)) [])
where
go
:: TwoZip String
-> [String]
-> [String]
go z@TwoZip{..} firsts =
let
firsts' =
if isTitle first
then second:firsts
else firsts
in case advance z of
Nothing -> firsts'
Just z' -> go z' firsts'
然后是一些(简化的)测试数据:
testData = map show [1,2,3,1,3,4,1,4,3]
isTitle = (== "1")
first_paragraphs = findFirsts isTitle testData
正如我们想要的那样:
λ> first_paragraphs
["2","3","4"]
你可能会弄清楚如何扩展它以找到 最后一段以及如何在一次迭代中完成它们。