Какая идиома Racket/Scheme для этого кода?

Я новичок в ракетке / схеме, поэтому я решил научиться внедрять эмулятор для DCPU-16, простого 16-битного процессора.

Мой вопрос таков: как лучше реализовать мое решение?

Это решение, которое я взломал, чтобы контролировать регистры процессора. Основная задача состояла в том, чтобы позволить функциям, которые изменяют регистр, быть соединенными вместе. Например:

; Increment value stored in register r-id
; returns the updated register
;
; Reg - the register structure 
; (reg-inc Reg 'SP)
(define (reg-inc reg r-id)
    (reg-write reg r-id (+ (reg-read reg r-id) 1 )))

; chain them together
;(reg-inc (reg-inc Reg 'SP)
;         'PC)
;
; returns structure with both 'SP and 'PC incremented

Полный текст моего решения о регистрации ниже. Моя полная программа также на github. Существует так много повторяющейся логики, я знаю, что должен быть более простой способ:

(struct registers (A B C X Y Z I J SP PC O Pa Pb Paadr Pbadr CLK)
  #:transparent)

(define Reg (registers 0 0 0 0 0 0 0 0 #x10000 0 0 0 0 0 0 0))

(define (reg-name n)
  (case n
    [(0) 'A]
    [(1) 'B]
    [(2) 'C]
    [(3) 'X]
    [(4) 'Y]
    [(5) 'Z]
    [(6) 'I]
    [(7) 'J]
    [(8) 'SP]
    [(9) 'PC]
    [(10) 'O]
    [(11) 'Pa]
    [(12) 'Pb]
    [(13) 'Paadr]
    [(14) 'Pbadr]
    [(15) 'CLK]
    [else (error "Invalid register")]))

(define (reg-id s)
  (cond
    [(eq? 'A s) 0]
    [(eq? 'B s) 1]
    [(eq? 'C s) 2]
    [(eq? 'X s) 3]
    [(eq? 'Y s) 4]
    [(eq? 'Z s) 5]
    [(eq? 'I s) 6]
    [(eq? 'J s) 7]
    [(eq? 'SP s) 8]
    [(eq? 'PC s) 9]
    [(eq? 'O s) 10]
    [(eq? 'Pa s) 11]
    [(eq? 'Pb s) 12]
    [(eq? 'Paadr s) 13]
    [(eq? 'Pbadr s) 14]
    [(eq? 'CLK s) 15]))

(define (reg-read reg r)
  (if (symbol? r)
      (reg-read reg (reg-id r))
      (case r
        [(0) (registers-A reg)]
        [(1) (registers-B reg)]
        [(2) (registers-C reg)]
        [(3) (registers-X reg)]
        [(4) (registers-Y reg)]
        [(5) (registers-Z reg)]
        [(6) (registers-I reg)]
        [(7) (registers-J reg)]
        [(8) (registers-SP reg)]
        [(9) (registers-PC reg)]
        [(10) (registers-O reg)]
        [(11) (registers-Pa reg)]
        [(12) (registers-Pb reg)]
        [(13) (registers-Paadr reg)]
        [(14) (registers-Pbadr reg)]
        [(15) (registers-CLK reg)]
        [else (error "Invalid register")])))

(define (reg-write reg r val)
  (if (symbol? r)
      (reg-write reg (reg-id r) val)
      (let ([mask-val (bitwise-and val #xffff)])
        (case r
          [(0) (struct-copy registers reg [A mask-val])]
          [(1) (struct-copy registers reg [B mask-val])]
          [(2) (struct-copy registers reg [C mask-val])]
          [(3) (struct-copy registers reg [X mask-val])]
          [(4) (struct-copy registers reg [Y mask-val])]
          [(5) (struct-copy registers reg [Z mask-val])]
          [(6) (struct-copy registers reg [I mask-val])]
          [(7) (struct-copy registers reg [J mask-val])]
          [(8) (struct-copy registers reg [SP mask-val])]
          [(9) (struct-copy registers reg [PC mask-val])]
          [(10) (struct-copy registers reg [O mask-val])]
          [(11) (struct-copy registers reg [Pa mask-val])]
          [(12) (struct-copy registers reg [Pb mask-val])]
          [(13) (struct-copy registers reg [Paadr mask-val])]
          [(14) (struct-copy registers reg [Pbadr mask-val])]
          [(15) (struct-copy registers reg [CLK mask-val])]
          [else (error "Invalid register")]))))

Обновить:

Благодаря предложениям oobviat я рефакторинг с использованием списков. Единственной сложностью было обновление значения в списке. Я написал процедуру для карты, которая обновит нужный регистр и оставит остальные с их первоначальным значением:

;; a-list of registers and initial values
(define (build-reg)
  '((A . 0)  (B . 0)     (C . 0)      (X . 0)
    (Y . 0)  (Z . 0)     (I . 0)      (J . 0)
    (SP . 0) (PC . 0)    (O . 0)      (Pa . 0)
    (Pb . 0) (Paadr . 0) (Pbadr . 0)  (CLK . 0)))

(define *REF-REG* (build-reg)) ; used to determine structure

(define (reg-name n)
  (if (symbol? n)
      n
      (car (list-ref *REF-REG* n))))

(define (reg-id s)
  (- (length *REF-REG*)
     (length (memf (lambda (arg)
                     (eq? s (car arg)))
                   *REF-REG*))))

(define (reg-write reg r val)
  (let ([r-name (reg-name r)])
    (define (reg-write-helper entry)
      (if (eq? r-name
               (car entry))
          (cons r-name val)
          entry))
    (map reg-write-helper reg)))

(define (reg-read reg r)
  (cdr (assoc (reg-name r) reg)))

1 ответ

Решение

Это не было написано в Racket, поэтому он может не работать для вас как есть... если он выдает ошибки, попробуйте указать тип кода R5RS в верхней части файла. Для простоты я бы сделал что-то подобное, используя a-list, а не структуры.

;; a-list of registers and initial values
(define *reg*
  '((A . 0) (B . 0) (C . 0) (X . 0) (Y . 0) (Z . 0)
    (I . 0) (J . 0) (SP . #X10000) (PC . 0) (O . 0)
    (Pa . 0) (Pb . 0) (Paadr . 0) (Pbadr . 0) (CLK . 0)))

(define (reg-write register val)
  (set-cdr! (assoc register *reg*) val) ;write new value to register
  val) ; return newly written value

(define (reg-read register)
  (cdr (assoc register *reg*)))

(define (reg-inc register)
  (reg-write register (+ 1 (reg-read register))))

;; to do many operations
;; input:  a list of registers
;;    EX:  '(a b x)
(define (do-incs registers)
  (if (null? registers)
      'done       ; return something when the incs are done
      (begin      ; lets you evaluate multiple expressions since `if` doesn't          
        (reg-inc (car registers))
        (do-incs (cdr registers)))))

Я предполагаю, что Ракетка имеет встроенный как assoc который возвращает правильный список из a-list. Также обратите внимание, что *reg* определяется как глобальная переменная в этом случае, так что мы можем просто определить ее один раз, а затем использовать set-cdr! записать значения в него.

наконец, это может сделать странные вещи для вашего SP регистр. Моя схема видит это как 65536.. если это не так, возможно, вам придется добавить if в reg-write а также reg-read чтобы убедиться, что вы получаете правильные значения там.

<EDIT>Итак, я немного прочитал о процедурах Racket, и этот код почти наверняка не будет работать в обычном Racket, потому что они, очевидно, имеют как изменяемые, так и неизменяемые пары. Изменения, которые вам придется внести, если вы хотите запустить это в Racket, а не в R5RS, следующие:

Вместо того, чтобы просто использовать список в кавычках, вам, вероятно, потребуется составить список регистров с помощью изменяемых конструкторов list/pair (define *reg* (mlist (mcons 'A 0) (mcons 'B 0) ... ),

Вместо того, чтобы использовать set-cdr! версия ракетки есть set-mcdr! и работает только на изменчивых парах.</EDIT>

Другие вопросы по тегам