简体   繁体   中英

Replacing a certain number of repeated elements in a list in Racket

I am trying to define the rule 3 of "MIU System" of "Gödel, Escher, Bach" (Douglas Hofstadter), which says:

Replace any III with a U

Example:

M III IU → MUIU and MI III U → MIUU 在此处输入图像描述

Main code:


    (define (rule-tree lst)
    (if (<= 3 (counter lst #\I))
        (append (delete #\I lst) (list #\U))
        (append lst empty)))
    
    (define (delete x lst)
      (cond [(empty? lst) lst]
            [(eq? (first lst) x) (delete x (rest lst))]
            [else (append (list (first lst)) (delete x (rest lst)))]))
    
    (define (counter lst target)
      (if (empty? lst)
          0
          (+ (counter (rest lst) target) 
             (let ((x (first lst)))
               (if (list? x)
                   (counter x target) 
                   (if (eqv? x target) 1 0))))))

With this expression there is no problem:

>(rule-tree '(#\M #\I #\I #\I))
'(#\M #\U)

But I don't know how to determine the position that the "U" should take when finding the 3 "I". Any suggestion will be very helpful:)

Here is an alternative recursive version, where repl2 encodes the information “we have just encountered one #\I ”, while repl3 encodes the information “we have just encountered two #\I ”:

(define (repl lst)
  (cond ((empty? lst) lst)
        ((eqv? (first lst) #\I) (repl2 (rest lst)))
        (else (cons (first lst) (repl (rest lst))))))

(define (repl2 lst)
  (cond ((empty? lst) (list #\I))
        ((eqv? (first lst) #\I) (repl3 (rest lst)))
        (else (cons #\I (cons (first lst) (repl (rest lst)))))))

(define (repl3 lst)
  (cond ((empty? lst) (list #\I #\I))
        ((eqv? (first lst) #\I) (cons #\U (repl (rest lst))))
        (else (cons #\I (cons #\I (cons (first lst) (repl (rest lst))))))))

Of course this solution is some kind of hack and cannot scale to a greater number of repetitions. But looking at the structure of this solution and simply generalizing the three functions we can produce a general solution:

(define (repl lst n from to)
  (define (helper lst k)
    (cond ((empty? lst) (repeat from (- n k)))
          ((eqv? (first lst) from)
           (if (= k 1)
               (cons to (helper (rest lst) n))
               (helper (rest lst) (- k 1))))
          (else (append (repeat from (- n k))
                        (cons (first lst) (helper (rest lst) n))))))    

(define (repeat x n)
  (if (= n 0)
      '()
      (cons x (repeat x (- n 1)))))

We define a function repl that takes a list, the number of copies to replace ( n ), the element to replace ( from ) and the element that must be substituted ( to ). Then we define a helper function to do all the work, and that has as parameters the list to be processed and the number of copies that must be still found ( k ).

Each time the function encounters a copy it checks if we have finished with the number of copies and substitutes the element, restarting its work, otherwise it decrements the number of copies to find and continues.

If it founds an element different from from it recreates the list with the elements “consumed” until this point (maybe 0) with repeat and then continues its work.

Note that the previous version of the helper function had an error in the final case, when lst is null. Instead of returning simply the empty list, we must return the possibly skipped from elements.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM