tristanhennequin <tristanhennequin@free.fr> writes:
Hello,
I 've just read the vigenere implementation published on rosetta code :
I think it's very long and maybe clumsy.
It's also wrong.
(defun strip (s)
(remove-if-not
(lambda (c) (char<= #\A c #\Z))
(string-upcase s)))
There may be non alphabetic characters between A and Z. This functions rightfully doesn't use alpha-char-p, (because alpha-char-p may contain implementation defined characters), but it is wrong in using this simple comparison. Since we only want letters from A to Z, we must write it explicitely (or, possibly check that only letters from A to Z are
between A and Z, but I have my doubts if this check can be done at compilation time).
So:
(defparameter alphabet "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
(defun strip (s)
(remove-if-not (lambda (c) (find (char-upcase c) alphabet)) s)
(defun vigenère (s key &key decipher
&aux (A (char-code #\A))
(op (if decipher #'- #'+)))
Personnally, I don't like using &aux for stylistic reasons (it puts in
the signature of the function details that belong to the implementation.
It's very rare to find a case where having those variable in the
signature is justified. &aux is basically a ugly hack to do stuff in
struct constructors).
(labels
((to-char (c) (code-char (+ c A)))
(to-code (c) (- (char-code c) A)))
Again, this part makes a lot of assumptions that may be wrong. A better implementation is:
(to-char (code) (aref code alphabet))
(to-code (char) (position char alphabet))
(let ((k (map 'list #'to-code (strip key))))
(setf (cdr (last k)) k)
(map 'string
(lambda (c)
(prog1
(to-char
(mod (funcall op (to-code c) (car k)) 26))
(setf k (cdr k))))
(strip s)))))
This is not bad. What you can do here, is to use pop:
(map 'string
(lambda (c) (to-char (mod (funcall op (to-code c) (pop k)) 26)))
(strip s))
(let* ((msg "Beware the Jabberwock... The jaws that... the claws that catch!")
(key "vigenere cipher")
(enc (vigenère msg key))
(dec (vigenère enc key :decipher t)))
(format t "msg: ~a~%enc: ~a~%dec: ~a~%" msg enc dec))
What is the most concise and short version of the vigenere encryption method can you produce in lisp?
(defparameter alphabet "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
(defun strip-non-letters (string)
(remove-if-not (lambda (c) (find (char-upcase c) alphabet)) string))
(defun ensure-circular (list)
(setf (cdr (last list)) list))
(defun vigenère (text key &key decipher)
(flet ((to-char (code) (aref alphabet code))
(to-code (char) (position char alphabet :test (function char-equal))))
(declare (inline to-char to-code))
(let ((op (if decipher (function -) (function +)))
(offsets (ensure-circular (map 'list (function to-code) (strip-non-letters key)))))
(map 'string
(lambda (c) (to-char (mod (funcall op (to-code c) (pop offsets)) 26)))
(strip-non-letters text)))))
(let* ((msg "Beware the Jabberwock... The jaws that... the claws that catch!")
(key "vigenere cipher")
(enc (vigenère msg key))
(dec (vigenère enc key :decipher t)))
(format t "msg: ~a~%enc: ~a~%dec: ~a~%" msg enc dec))
msg: Beware the Jabberwock... The jaws that... the claws that catch!
enc: WMCEEIKLGRPIFVMEUGXXYILILZXYVBZLRGCEYAIOEKXIZGU
dec: BEWARETHEJABBERWOCKTHEJAWSTHATTHECLAWSTHATCATCH
nil
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 418 |
Nodes: | 16 (2 / 14) |
Uptime: | 17:52:34 |
Calls: | 8,804 |
Calls today: | 2 |
Files: | 13,304 |
Messages: | 5,969,460 |