(define-module (pp) #:use-module (srfi srfi-1) #:export (pp parse-line count-occurrences valid-password-p1? valid-password-p2?)) (define (pp str p1?) (count (lambda (x) x) (map (lambda (line) (let ((parsed (parse-line line))) (if p1? (valid-password-p1? (car parsed) (cadr parsed) (caddr parsed)) (valid-password-p2? (car parsed) (cadr parsed) (caddr parsed))))) (string-split (string-trim-both str char-set:whitespace) #\newline)))) (define (parse-line str) "Parses a line of the puzzle format into a workable datastructure: (passwd (char (min max)))" (let* ((fields (string-split str char-set:whitespace)) (range (map string->number (string-split (car fields) #\-))) (char (string-ref (cadr fields) 0)) (passwd (caddr fields))) (list passwd char (cons (car range) (cadr range))))) (define (valid-password-p2? passwd char range) "Wrapper for valid-password-p1?. Prepares the input by indexing RANGE into PASSWD, setting that as the new PASSWD, and setting RANGE to (1 . 1) Note the original problem specifies that strings are NOT zero-indexed!" (valid-password-p1? (string (string-ref passwd (1- (car range))) (string-ref passwd (1- (cdr range)))) char '(1 . 1))) (define (valid-password-p1? passwd char range) "Given PASSWD, checks that CHAR appears within RANGE number of times. RANGE is a cons pair." (let ((occurrences (count-occurrences passwd char))) (and (>= occurrences (car range)) (>= (cdr range) occurrences)))) (define (count-occurrences str char) "Returns the number of times CHR appears in STR." (string-length (string-filter char str)))