#lang Scheme ;; Language level = Module ;; hash-items: hashtable(any-->any) --> list of (any, any) ;; Returns a list of all the key-value pairs in a hashtable. (define (hash-items ht) (hash-map ht (lambda (k v) (list k v)))) ;; avail_chars: list-of-chars ;; A list of characters to be used for encoding/decoding (define avail_chars (string->list "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,!? ")) (define SPACE_CHAR 32) ;; ASCII code for space character (define DEL_CHAR 127) ;; ASCII code for del character ;; makeCharList: int int --> list-of-chars ;; (makeCharList startCharInt stopCharInt) Returns a list of characters with consecutive ASCII numerical values, ;; starting with the integer value, startCharInt, inclusive up to stopCharInt, exclusive. (define (makeCharList startCharInt stopCharInt) (cond [(>= startCharInt stopCharInt) empty] [else (cons (integer->char startCharInt) (makeCharList (add1 startCharInt) stopCharInt))])) ;; remove-nth: list-of-any integer --> list-of-any ;; (remove-nth loa n) returns a list where the n'th element (starting at zero) from ;; loa has been removed. ;; Throws an exception if the n'th element doesn't exist. (define (remove-nth loa n) (cond [(empty? loa) (raise "remove-nth: Too few elements in list!")] ; no elements to remove here! [(zero? n) (rest loa)] ; found the n'th element, return the rest of the list [else (cons (first loa) (remove-nth (rest loa) (sub1 n)))])) ; not here. Count down into rest. ;; makeEncoderDict: list-of-chars --> hashtable(char-->char) ;; (makeEncoderDict srcChars) returns a hash table who's keys are the characters ;; in srcChars where the associated value is another, randomly chosen character ;; from the same set. (define (makeEncoderDict srcChars) (local [(define dict (make-hash)) ; The dictionary that will be returned. ;; fillDict: list-of-chars --> hashtable(char-->char) ;; (fillDict srcChars) is a recursive helper function that returns the above dict hash table ;; who's keys are the characters in srcChars where the associated value is another, ;; randomly chosen character from the same set. (define (fillDict remainingChars) (local [(define nAvail (length remainingChars))] ; this value is needed several times below (cond [(zero? nAvail) dict] ; done! return the filled dictionary [else (local [(define valueIdx (random nAvail))] ; pick a random number 0<= valueIdx < n (begin ; associate the n-1'th element of the srcChars list with ; the i'th member of the remaining characters (hash-set! dict (list-ref srcChars (sub1 nAvail)) (list-ref remainingChars valueIdx)) ; recurse onto the remaining characters with the i'th char removed. (fillDict (remove-nth remainingChars valueIdx))))])))] (fillDict srcChars))) ; Start the process off with the full list of characters ;; encode: string hashtable(char-->char) --> string ;; (encode str dict) returns a string where every character ;; in str has been replaced by the corresponding value in dict. (define (encode str dict) (list->string (map (lambda (c) (hash-ref dict c)) (string->list str)))) ;; makeDecoderDict: hashtable(any-->any) --> hashtable(any-->any) ;; (makeDecoderDict inputDict) inverts the input hashtable, inputDict, ;; returning a new hashtable where the keys and values from inputDict been ;; swapped. For instance if in inputDict, key x --> value y, then in the ;; returned hashtable, key y --> value x. ;; This version first converts inputDict into a list of key-value pairs and ;; then maps a lambda over the list to fill the outputDict. (define (makeDecoderDict inputDict) (local [(define outputDict (make-hash))] (begin (map (lambda (kv) (hash-set! outputDict (second kv) (first kv))) (hash-items inputDict)) outputDict))) ;; makeDecoderDict2: hashtable(any-->any) --> hashtable(any-->any) ;; (makeDecoderDict2 inputDict) inverts the input hashtable, inputDict, ;; returning a new hashtable where the keys and values from inputDict been ;; swapped. For instance if in inputDict, key x --> value y, then in the ;; returned hashtable, key y --> value x. ;; This version directly maps a lambda over the inputDict to fill the outputDict. (define (makeDecoderDict2 inputDict) (local [(define outputDict (make-hash))] (begin (hash-map inputDict (lambda (k v) (hash-set! outputDict v k))) outputDict))) ;; ------- Test code ----------------------- (define allChars (makeCharList SPACE_CHAR DEL_CHAR)) (define encoderDict1 (makeEncoderDict allChars)) (define decoderDict1 (makeDecoderDict encoderDict1)) (define encoderDict2 (makeEncoderDict allChars)) (define decoderDict2 (makeDecoderDict2 encoderDict2)) (define str "The quick brown fox jumps over the lazy dog!") (string=? (encode (encode str encoderDict1) decoderDict1) str) ;; true (string=? (encode (encode str encoderDict1) decoderDict2) str) ;; false -- wrong decoder (string=? (encode (encode str encoderDict2) decoderDict1) str) ;; false -- wrong decoder (string=? (encode (encode str encoderDict2) decoderDict2) str) ;; true