Haskell “哈斯克尔”;“弦乐运动”;功能
我正在尝试使用Haskell、hscurses和Data.text构建一个简单的文本编辑器。我对哈斯克尔是个新手 以下是我的代码片段:Haskell “哈斯克尔”;“弦乐运动”;功能,haskell,Haskell,我正在尝试使用Haskell、hscurses和Data.text构建一个简单的文本编辑器。我对哈斯克尔是个新手 以下是我的代码片段: data Cursor = Cursor { position :: Int, line :: Int, column :: Int } deriving (Eq, Show) isNewline :: Char -> Bool isNewline c = c == '\n' onNewline :: T.Text ->
data Cursor = Cursor {
position :: Int,
line :: Int,
column :: Int
} deriving (Eq, Show)
isNewline :: Char -> Bool
isNewline c = c == '\n'
onNewline :: T.Text -> Int -> Bool
onNewline buf pos
| pos >= T.length buf = False
| otherwise = isNewline $ T.index buf pos
findIndex :: (Char -> Bool) -> T.Text -> Int -> Maybe Int
findIndex pred buf pos
| buf == T.empty = Just 0
| otherwise = rightWhile pos
where rightWhile pos
| pos > bufMax buf = Nothing
| pred $ T.index buf pos = Just pos
| otherwise = rightWhile (pos + 1)
findIndexLeft :: (Char -> Bool) -> T.Text -> Int -> Maybe Int
findIndexLeft pred buf pos = leftWhile pos
where leftWhile pos
| pos < 0 = Nothing
| pred $ T.index buf pos = Just pos
| otherwise = leftWhile (pos - 1)
startOfLine :: T.Text -> Int -> Int
startOfLine buf pos = case findIndexLeft isNewline buf (pos - 1) of
Nothing -> 0
Just p -> p + 1
endOfLine :: T.Text -> Int -> Int
endOfLine buf pos = case findIndex isNewline buf pos of
Nothing -> 1 + bufMax buf
Just p -> p
lineOffset :: T.Text -> Int -> Int
lineOffset buf pos = pos - startOfLine buf pos
lineLength :: T.Text -> Int -> Int
lineLength buf pos = endOfLine buf pos - startOfLine buf pos
bufMax :: T.Text -> Int
bufMax buf = max 0 $ T.length buf - 1
bufLines :: T.Text -> Int
bufLines = T.foldl (\acc c -> if isNewline c then (acc+1) else acc) 0
moveCursorRight :: T.Text -> Cursor -> Cursor
moveCursorRight buf c@(Cursor pos line col)
| buf == T.empty = c
| otherwise = Cursor newPos newLine newCol
where end = 1 + bufMax buf
onEnd = pos == end
newPos = clip (pos + 1) 0 end
newLine = if onNewline buf pos && not onEnd
then line + 1
else line
newCol = lineOffset buf newPos
moveCursorLeft :: T.Text -> Cursor -> Cursor
moveCursorLeft buf (Cursor pos line col) =
Cursor newPos newLine newCol
where onStart = pos == 0
newPos = clipLow (pos - 1) 0
newLine = if onNewline buf newPos && not onStart
then line - 1
else line
newCol = lineOffset buf newPos
-- More movement functions follow...
但这并没有太大帮助,因为“at”必须与“before”连用,而且根据文档,t.cons
是O(n)。。。此外,当实际显示完成时,以线为中心的方法更好
感谢所有帮助过我的人 正如gallais在评论中所说,你想使用拉链。其思想是,您的“游标”实际上是一个如下所示的数据结构:
type Line = T.Text
data TextZipper = TextZipper {
textBefore :: [Line],
currentLine :: Line,
textAfter :: [Line]
}
moveDown :: TextZipper -> Maybe TextZipper
moveDown tzip = case textAfter tzip of
[] -> Nothing -- Already at the bottom of the file.
t:ts -> TextZipper {
textBefore = currentLine tzip : textBefore tzip,
currentLine = t,
textAfter = ts
}
诀窍是“textbevere”以相反的顺序包含光标上方的行。因此,要向下移动一行,请将“currentLine”放在“textBefore”的开头,并从“textAfter”中取出新的“currentLine”,如下所示:
type Line = T.Text
data TextZipper = TextZipper {
textBefore :: [Line],
currentLine :: Line,
textAfter :: [Line]
}
moveDown :: TextZipper -> Maybe TextZipper
moveDown tzip = case textAfter tzip of
[] -> Nothing -- Already at the bottom of the file.
t:ts -> TextZipper {
textBefore = currentLine tzip : textBefore tzip,
currentLine = t,
textAfter = ts
}
moveUp将非常类似。您还需要一个textZipperToList函数来提取拉链的内容以进行保存,还需要一个textZipperFromList函数
我记得在某个地方读到,Emacs使用了一个类似的概念,只是它是通过字符而不是行来实现的。缓冲区表示为两个文本块,一个在光标前,一个在光标后。移动光标是通过将字符从一个块复制到另一个块来完成的。这里也有同样的概念。考虑到这一点,您可能需要考虑将每个行列表替换为单个文本值。您可能需要将缓冲区表示为行列表,使用表示当前位置的。对于Agda中的一个指导性示例(您可以在Haskell中复制相当多的工作),您可以查看另一个请参见:。有一种方法可以将
findIndex
加速到O(n),但之后您仍然使用索引位置,因此从长远来看,这对您没有帮助。如果您需要跳转,您应该使用Data.sequence
从列表拉链切换到序列拉链<代码>数据拉链a=拉链(序列a)a(序列a)或类似产品。不像列表拉链,你可以保持一切有序,因为结束和开始一样快。您可以在O(logn)
时间中移动n
步骤。