Arc Forumnew | comments | leaders | submitlogin
A "scanner" abstraction.
2 points by almkglor 6114 days ago | 1 comment
Reading through the old arcsug.txt http://www.archub.org/arcsug.txt

I remember reading this idea about "scanners" by Stephen Ma, which were basically just a string with an index parameter. It seemed somewhat overkill at the time, since you could always store the index cheaply anyway. cref. 'markdown

However, recently I thought of a rather nice application for scanners: when the string isn't a basic Arc string.

Suppose I've divided a string into words, for purposes of, hmm, calculating the diff between versions of an article ^^. I could store the original article string as my primary representation and compute the word-divided string from it. However, this means that during diff operations I need to store about twice the data I need: the original string representation, and the word-divided version.

Now suppose instead that I make my primary representation the word-divided version. This then loses the otherwise-neat ability to access string elements by (str index). I could create a function that simulates this, but then I'd have to compute which word a particular index is in each time.

However if instead I could build my own scanner, providing only the 'car and 'cdr interfaces (not 'scar and 'scdr, it wouldn't scan), then I could trivially compute indices: at each 'cdr I increment the index in the current word, and if the current word is ended I move to the next word (a single comparison to a single length).

Basically if I had this::

  (def scanner-for-list-of-words (ws (o index 0))
    (with (l (- (len:car ws) index)
              w (car ws)
              d nil)
      (scanner
        'car (fn () (w index))
        'cdr
        (fn ()
          (or d
              (= d
                (if (is l 1)
                  (scanner-for-list-of-words (cdr ws))
                  (scanner-for-list-of-words ws (+ index 1)))))))
'scanner would be trivially writeable using my settable-fn's as:

  (def scanner args
    (let pargs (pair args)
      (add-attachment
        'car (cadr:alist 'car pargs)
        'cdr (cadr:alist 'cdr pargs)
        (list))))

  (redef car (c)
    (aif (get-attachment 'car c)
         (it)
         (old c)))

  (redef cdr (c)
    (aif (get-attachment 'cdr c)
         (it)
         (old c)))
Other implementations are of course possible - for example by tagging scanners as their own type and having 'car/'cdr dispatch from the type (although this will probably complicate the use of 'each if you want to scan with that).


2 points by almkglor 6114 days ago | link

Hmm, not getting anyone excited I suppose. ^^

Now suppose that I told everyone that using a scanner would allow us to use --warning-blatant-self-promotion-- my p-m: modifier system:

  (def get-word (s)
    (let (pre-word word build hd tl) nil
      (= build
         (fn (c)
           (if hd (do (= (cdr tl) (cons c nil)) (= tl (cdr tl)))
                  (do (= hd (cons c nil)) (= tl hd))))
         pre-word
         (p-m:fn
           ( (,@(whitec _) . ss) ) (pre-word ss)
           (ss) (word ss))
         word
         (p-m:fn
           ( (,(c (nonwhite c)) . ss) ) (do (build c) (word ss))
               (string hd)))
      (pre-word (scanner-for-string s))))
This is because p-m uses only 'car and 'cdr to decompose the input list ^^

-----