From 7873fda361486bc1ec2f9323309dec0a0ca239f0 Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Tue, 17 Dec 2019 11:26:03 -0800 Subject: [PATCH] Refactored IntCode to internally use a hashmap instead of a vector. --- lib.rkt | 32 +++++++++++++++++++++++--------- src/IntCode.rkt | 49 ++++++++++++++++++++++++++----------------------- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/lib.rkt b/lib.rkt index 82d8382..a9e577e 100644 --- a/lib.rkt +++ b/lib.rkt @@ -12,7 +12,7 @@ make-vector-grid vectors->lists - hash->vector + hash->vectors show-list-grid show-vector-grid show-hash-grid @@ -38,7 +38,9 @@ list->queue vector-ref* - vector-set!*) + vector-set!* + hash->vector + vector->hash) ;; Function helpers ;; @@ -60,7 +62,7 @@ [path (string-append "../input/" filename ".txt")]) (read-lines path))) -;; show-solution : any/c -> any/c -> void +;; show-solution : a -> b -> void ;; Print part1 and part2 on separate lines. (define (show-solution part1 part2) (printf "Part 1: ~a\nPart 2: ~a\n" part1 part2)) @@ -80,10 +82,10 @@ (define (vectors->lists vector-grid) (map vector->list (vector->list vector-grid))) -;; hash->vector : hash-grid -> number -> vector-grid +;; hash->vectors : hash-grid -> number -> vector-grid ;; Where the position is not in the hash-grid, ;; the vector-grid takes on the default value. -(define (hash->vector hash-grid [default 0]) +(define (hash->vectors hash-grid [default 0]) (let* ([keys (hash-keys hash-grid)] [xs (map car keys)] [ys (map cdr keys)] @@ -113,7 +115,7 @@ ;; show-hash-grid : (hashof (value => char)) -> hash-grid -> number -> void (define (show-hash-grid char-hash hash-grid [default 0]) - (show-vector-grid char-hash (hash->vector hash-grid default))) + (show-vector-grid char-hash (hash->vectors hash-grid default))) ;; Number helpers ;; @@ -189,7 +191,7 @@ (cons (f v (first lst)) lst)) (list init) lst))) -;; list-ref* : (listof any) -> number -> any -> any +;; list-ref* : (listof a) -> number -> a -> a ;; Same as list-ref, except a default value is provided ;; if the index is beyond the length of the list. (define (list-ref* lst pos failure-result) @@ -224,7 +226,7 @@ [lists (map (λ (lst) (take lst min-len)) lists)]) (apply map list lists))) -;; list->queue : (listof any) -> (queueof any) +;; list->queue : (listof a) -> (queueof a) ;; Creates a queue and adds elements of list in order (define (list->queue lst) (let ([Q (make-queue)]) @@ -251,4 +253,16 @@ (let ([new-vec (make-vector (max (vector-length vec) (add1 pos)))]) (vector-copy! new-vec 0 vec) (vector-set! new-vec pos v) - new-vec)) \ No newline at end of file + new-vec)) + +;; hash->vector : (hashof (number => a)) -> (vectorof a) +;; Convert an intmap into a mutable vector +(define (hash->vector hash [default 0]) + (let ([length (add1 (apply max (hash-keys hash)))]) + (build-vector length (λ (i) (hash-ref hash i default))))) + +;; vector->hash : (vectorof a) -> (hashof (number => a)) +;; Convert a vector into an immutable intmap +(define (vector->hash vec) + (let ([kvs (map cons (range (vector-length vec)) (vector->list vec))]) + (make-immutable-hash kvs))) \ No newline at end of file diff --git a/src/IntCode.rkt b/src/IntCode.rkt index 4010eea..fd5515e 100644 --- a/src/IntCode.rkt +++ b/src/IntCode.rkt @@ -3,17 +3,17 @@ (require racket/vector "../lib.rkt") -(define (vector-ref vec pos) - (vector-ref* vec pos 0)) - -;; string->program : string -> (listof number) +;; string->program : string -> (vectorof number) ;; A program is a list of numbers, ;; which are sequences of instructions and parameters. (define (string->program str) (list->vector (map string->number (string-split str ",")))) +(define (hash-ref* hash key) + (hash-ref hash key 0)) + (define (program? p) - (vectorof number?)) + hash?) ;; state = ;; | out number (() -> state) @@ -38,21 +38,21 @@ [in (resume) (resume input)] [else (error "resume-with-input: Unexpected program state.")])) -;; resume-with-io : state -> (listof number) -> (listof number) +;; resume-with-io : state -> (listof number) -> (listof number) -> (listof number) ;; Run the program, providing input as needed, and collecting output. -(define (resume-with-io st inputs) +(define (resume-with-io st inputs [outputs '()]) (type-case state st [in (resume) - (resume-with-io (resume (car inputs)) (cdr inputs))] + (resume-with-io (resume (car inputs)) (cdr inputs) outputs)] [out (value resume) - (cons value (resume-with-io (resume) inputs))] - [halt (program) '()])) + (resume-with-io (resume) inputs (cons value outputs))] + [halt (program) (reverse outputs)])) -;; halt-with-program : state -> program +;; halt-with-program : state -> (vectorof number) ;; Return program state of halted execution. (define (halt-with-program st) (type-case state st - [halt (program) program] + [halt (program) (hash->vector program)] [else (error "halt-with-program: Unexpected program state.")])) ;; exec* : program -> number -> number -> state @@ -74,7 +74,7 @@ ;; If the mode is 2, the value at pointer is an address to be offset by base. ;; Note that leading zeroes in the encoded instruction are omitted. (define (exec* program #:ptr [pointer 0] #:base [base 0]) - (define instruction (vector-ref program pointer)) + (define instruction (hash-ref* program pointer)) (define opcode (remainder instruction 100)) (define next-pointer (match opcode @@ -84,9 +84,9 @@ [99 (+ pointer 1)])) (define (get-location index mode) (match mode - [0 (vector-ref program (+ pointer index))] + [0 (hash-ref* program (+ pointer index))] [1 (+ pointer index)] - [2 (+ (vector-ref program (+ pointer index)) base)])) + [2 (+ (hash-ref* program (+ pointer index)) base)])) (let* ([mode1 (remainder (quotient instruction 100) 10)] [mode2 (remainder (quotient instruction 1000) 10)] [mode3 (remainder (quotient instruction 10000) 10)] @@ -95,19 +95,19 @@ [l2 (λ () (get-location 2 mode2))] [l3 (λ () (get-location 3 mode3))] ;; v* : call to read values from program - [v1 (λ () (vector-ref program (l1)))] - [v2 (λ () (vector-ref program (l2)))] - [v3 (λ () (vector-ref program (l3)))]) + [v1 (λ () (hash-ref* program (l1)))] + [v2 (λ () (hash-ref* program (l2)))] + [v3 (λ () (hash-ref* program (l3)))]) (match opcode [(or 1 2) (let* ([arith (match opcode [1 +] [2 *])] [value (arith (v1) (v2))] - [program (vector-set!* program (l3) value)]) + [program (hash-set program (l3) value)]) (exec* program #:ptr next-pointer #:base base))] [3 (let* ([resume (λ (input) - (let ([program (vector-set!* program (l1) input)]) + (let ([program (hash-set program (l1) input)]) (exec* program #:ptr next-pointer #:base base)))]) (in resume))] [4 @@ -122,7 +122,7 @@ [(or 7 8) (let* ([lt-eq (match opcode [7 <] [8 =])] [value (if (lt-eq (v1) (v2)) 1 0)] - [program (vector-set!* program (l3) value)]) + [program (hash-set program (l3) value)]) (exec* program #:ptr next-pointer #:base base))] [9 (let ([base (+ base (v1))]) @@ -130,6 +130,9 @@ [99 (halt program)]))) -;; Just so we always run the program on a fresh copy +;; The external interface accepts a vector, +;; while the internals use an immutable hashmap +;; for functional purity and performance (define (exec program #:ptr [pointer 0] #:base [base 0]) - (exec* (vector-copy program) #:ptr pointer #:base base)) \ No newline at end of file + (let* ([hash-program (vector->hash program)]) + (exec* hash-program #:ptr pointer #:base base))) \ No newline at end of file