38

टेक्स्ट संपादकों के लिए पूरी तरह से कार्यात्मक डेटा संरचनाएं क्या होंगी? मैं पाठ में एकल वर्णों को सम्मिलित करने और स्वीकार्य दक्षता वाले पाठ से एकल वर्णों को हटाने में सक्षम होना चाहता हूं, और मैं पुराने संस्करणों को पकड़ने में सक्षम होना चाहता हूं, इसलिए मैं आसानी से परिवर्तन पूर्ववत कर सकता हूं।पाठ संपादकों के लिए शुद्ध रूप से कार्यात्मक डेटा संरचना

क्या मुझे केवल तारों की एक सूची का उपयोग करना चाहिए और उन पंक्तियों का पुन: उपयोग करना चाहिए जो संस्करण से संस्करण में नहीं बदलते हैं?

+14

आप .. ज़िपर पर गौर कर सकते – Satvik

+3

आप जीयूआई ढांचे का उपयोग कर रहे हैं (जब तक आप एक सांत्वना आधारित एप्लिकेशन की योजना बना रहे) संभाल हो सकता है द्वारा विवश किया जा सकता है टिन से सीधे आपके लिए सभी पाठ संपादन। – AndrewC

+1

सूची जिपर देखो। –

उत्तर

10

Vector[Vector[Char]] शायद एक अच्छी शर्त होगी। यह IndexedSeq है, इसलिए आपके द्वारा उल्लेख किए गए List के विपरीत, सभ्य अपडेट/प्रीपेड/अपडेट प्रदर्शन है। यदि आप Performance Characteristics देखते हैं, तो यह एकमात्र अपरिवर्तनीय संग्रह है जिसका उल्लेख निरंतर स्थिर समय अपडेट है।

+2

मैंने कोशिश की, और यह बहुत अच्छा काम करता है! – fredoverflow

1

संभावनाओं जो मन में वसंत हैं:

  1. एक संख्यात्मक सूचकांक के साथ "पाठ" प्रकार। यह बफर की एक लिंक्ड सूची में टेक्स्ट रखता है (आंतरिक प्रतिनिधित्व यूटीएफ 16 है), इसलिए सिद्धांत में जबकि इसकी कम्प्यूटेशनल जटिलता आमतौर पर एक लिंक्ड सूची (जैसे इंडेक्सिंग ओ (एन) है), व्यावहारिक रूप से पारंपरिक लिंक से बहुत तेज़ है सूची है कि आप शायद एन के प्रभाव के बारे में भूल सकते हैं जब तक कि आप अपने बफर में पूरे विकिपीडिया को संग्रहीत नहीं कर लेते। यह देखने के लिए कि क्या मैं सही हूं (कुछ मैंने वास्तव में नहीं किया है, बीटीडब्लू) को देखने के लिए 1 मिलियन चरित्र पाठ पर कुछ प्रयोगों का प्रयास करें।

  2. एक टेक्स्ट जिपर: एक पाठ तत्व में कर्सर के बाद पाठ को संग्रहित करें, और पाठ कर्सर को दूसरे में पहले रखें। एक तरफ कर्सर स्थानांतरण पाठ को एक तरफ ले जाने के लिए।

53

मुझे नहीं पता कि यह सुझाव "अच्छा" की परिष्कृत परिभाषाओं के लिए "अच्छा" है, लेकिन यह आसान और मजेदार है। मैं अक्सर प्रदान करता हूं जो प्रतिपादन कोड के साथ जोड़ने, Haskell में एक पाठ संपादक के मूल लिखने के लिए एक अभ्यास सेट। डेटा मॉडल निम्नानुसार है।

सबसे पहले, मैं परिभाषित करता हूं कि x -elements की सूची में कर्सर होने के लिए यह क्या है, जहां कर्सर पर उपलब्ध जानकारी कुछ प्रकार m है। (x बाहर हो जाएगा Char या String किया जाना है।)

type Cursor x m = (Bwd x, m, [x]) 

यह Bwd बात सिर्फ पिछड़े "snoc-सूची" है। मैं मजबूत स्थानिक अंतर्ज्ञान रखना चाहता हूं, इसलिए मैं अपने कोड में चीजों को बदलता हूं, न कि मेरे सिर में। विचार यह है कि कर्सर के नजदीक सामान सबसे आसानी से सुलभ है। यही जिपर की भावना है।

data Bwd x = B0 | Bwd x :< x deriving (Show, Eq) 

मैं एक नि: शुल्क सिंगलटन प्रकार कर्सर के लिए एक पठनीय मार्कर के रूप में कार्य करने के लिए प्रदान करते हैं ...

data Here = Here deriving Show 

... और मुझे इस प्रकार कह सकते हैं कि यह एक String

में कहीं हो रहा है
type StringCursor = Cursor Char Here 

अब, कई लाइनों की एक बफर प्रतिनिधित्व करने के लिए, हम String रों ऊपर और कर्सर के साथ रेखा से नीचे की जरूरत है, और बीच में एक StringCursor, लाइन के लिए हम वर्तमान में संपादन कर रहे हैं।

type TextCursor = Cursor String StringCursor 

यह TextCursor प्रकार सब मैं संपादन बफ़र की स्थिति को दर्शाने के लिए है। यह एक दो परत जिपर है। मैं छात्रों को एएनएसआई-एस्केप-सक्षम शेल विंडो में टेक्स्ट पर व्यूपोर्ट प्रस्तुत करने के लिए कोड प्रदान करता हूं, यह सुनिश्चित करता है कि व्यूपोर्ट में कर्सर हो। उन्हें बस इतना करना है कि कुंजीस्ट्रोक के जवाब में TextCursor अपडेट करने वाले कोड को लागू करें।

handleKey :: Key -> TextCursor -> Maybe (Damage, TextCursor) 

जहां handleKeyNothing लौटना चाहिए अगर कीस्ट्रोक व्यर्थ है, लेकिन अन्यथा Just देने एक अद्यतन TextCursor और एक "क्षति रिपोर्ट",

data Damage 
    = NoChange  -- use this if nothing at all happened 
    | PointChanged -- use this if you moved the cursor but kept the text 
    | LineChanged -- use this if you changed text only on the current line 
    | LotsChanged -- use this if you changed text off the current line 
    deriving (Show, Eq, Ord) 

के उत्तरार्द्ध से एक होने (आप सोच रहे हैं कि Nothing लौटने और Just (NoChange, ...) लौटने के बीच क्या अंतर है, इस पर विचार करें कि क्या आप संपादक को बीप जाने के लिए भी चाहते हैं।) क्षति रिपोर्ट प्रस्तुतकर्ता को बताती है कि प्रदर्शित छवि को अद्यतित करने के लिए कितना काम करना है।

Key प्रकार केवल कच्चे एएनएसआई एस्केप दृश्यों से दूर सारणित संभावित कीस्ट्रोक को एक पठनीय डेटाटाइप प्रतिनिधित्व देता है। यह अपरिहार्य है।

मैं किट के इन हिस्सों को देकर इस डेटा मॉडल के साथ के बारे में ऊपर जाने के लिए एक बड़ा सुराग और नीचे तक छात्रों प्रदान करते हैं:

deactivate :: Cursor x Here -> (Int, [x]) 
deactivate c = outward 0 c where 
    outward i (B0, Here, xs)  = (i, xs) 
    outward i (xz :< x, Here, xs) = outward (i + 1) (xz, Here, x : xs) 

deactivate समारोह एक Cursor से बाहर फोकस शिफ्ट करने के लिए, दे रही है प्रयोग किया जाता है आप एक सामान्य सूची है, लेकिन आपको बता रहे हैं कि कर्सर था। इसी activate समारोह एक सूची में दी गई स्थिति पर कर्सर रखने के लिए प्रयास करता है:

activate :: (Int, [x]) -> Cursor x Here 
activate (i, xs) = inward i (B0, Here, xs) where 
    inward _ c@(_, Here, [])  = c -- we can go no further 
    inward 0 c     = c -- we should go no further 
    inward i (xz, Here, x : xs) = inward (i - 1) (xz :< x, Here, xs) -- and on! 

मैं छात्रों की पेशकश handleKey

handleKey :: Key -> TextCursor -> Maybe (Damage, TextCursor) 
handleKey (CharKey c) (sz, 
         (cz, Here, cs), 
         ss) 
    = Just (LineChanged, (sz, 
         (cz, Here, c : cs), 
         ss)) 
handleKey _ _ = Nothing 

की जान-बूझकर गलत और अधूरी परिभाषा जो सिर्फ साधारण चरित्र कीस्ट्रोक्स लेकिन संभालती है पाठ पीछे की तरफ आता है। यह देखना आसान है कि cके पर दिखाई देता है। मैं उन्हें बग को ठीक करने और तीर कुंजी, बैकस्पेस, डिलीट, रिटर्न इत्यादि के लिए कार्यक्षमता जोड़ने के लिए आमंत्रित करता हूं।

यह अब तक का सबसे कुशल प्रतिनिधित्व नहीं हो सकता है, लेकिन यह पूरी तरह से कार्यात्मक है और कोड को संपादित किए जा रहे पाठ के बारे में हमारे स्थानिक अंतर्ज्ञानों को ठोस रूप से अनुरूप बनाने में सक्षम बनाता है।

+0

कोड वर्तमान में 'darcs क्लोन http: // personal.cis.strath.ac.uk/conor.mcbride/CS410' के माध्यम से प्राप्त किया जा सकता है, http://www.reddit.com/r/haskell/comments/11pwh6 के अनुसार /। – nobody

7

हम यी में एक पाठ ज़िपर, हास्केल में एक गंभीर पाठ संपादक कार्यान्वयन का उपयोग करें।

अपरिवर्तनीय राज्य प्रकार के कार्यान्वयन निम्नलिखित में वर्णन किया गया,

http://publications.lib.chalmers.se/records/fulltext/local_94979.pdf

http://publications.lib.chalmers.se/records/fulltext/local_72549.pdf

और अन्य कागजात।

2

Clojure समुदाय आदि डेटा की वैक्टर के लिए एक लगातार डेटा strcuture कि कुशलतापूर्वक/कटा हुआ concatenated जा सकती है/डाला के रूप में RRB Trees (आराम मूलांक बैलेंस्ड) में दिख रही है

यह अनुमति देता है संयोजन, सम्मिलित-एट-सूचकांक और ओ (लॉग एन) समय में विभाजित संचालन।

मुझे लगता है कि चरित्र डेटा के लिए विशेष रूप से आरआरबी ट्री विशेष रूप से बड़े "संपादन योग्य" टेक्स्ट डेटा संरचनाओं के लिए उपयुक्त होगा।

4

मैं zippers का उपयोग Data.Sequence.Seq के साथ संयोजन में करने का सुझाव देता हूं जो finger trees पर आधारित है। तो आप के रूप में

data Cursor = Cursor { upLines :: Seq Line 
        , curLine :: CurLine 
        , downLines :: Seq Line } 

यह आपको/नीचे एक पंक्ति कर्सर को ऊपर ले जाने के लिए हे (1) जटिलता देता है वर्तमान स्थिति का प्रतिनिधित्व कर सकता है, और splitAt और (><) (संघ) के बाद से दोनों हे (लॉग है (न्यूनतम (एन 1, एन 2))) जटिलता, आपको ओ (लॉग (एल))एल लाइनों को ऊपर/नीचे छोड़ने के लिए जटिलता मिलेगी।

कर्सर के पहले और बाद में चरित्र के अनुक्रम को रखने के लिए आपके पास CurLine के लिए एक समान जिपर संरचना हो सकती है।

Line कुछ स्पेस-कुशल हो सकता है, जैसे ByteString