Newbie needs help please

Discussion of Common Lisp
RaydPanda
Posts: 8
Joined: Thu Oct 22, 2009 11:13 am
Location: Bergen, Norway

Re: Newbie needs help please

Post by RaydPanda » Tue Nov 03, 2009 12:38 pm

It works! At least on single words. But non the less, thank you! I really didn't think I'd ever come this far, you helped me a lot and gave me the right hints.

My program looks now like this:

Code: Select all

; 1) Program that takes in a word(given as a string) plus a number and encrypts the word using Caesar's Cipher in the modulo given as the number:

; Alphabet as parameter plus one whitespace at index 0

(defparameter *alphabet* " abcdefghijklmnopqrstuvwxyz,.0123456789")

; Finds the index of char in  *alphabet*:

(defun alphabet-index (char)
  (position char *alphabet*))
 
; Finds the char shifted n places to the right in the alphabet:

(defun rotate (char n)
  (elt *alphabet* (mod (+ (alphabet-index char) n)(length *alphabet*))))

; makes the rotate function accesable for map

(defun e-helper (n)
  #'(lambda (char)
      (rotate char n)))


; Returns the encrypted string
 
(defun encrypt (str n)
  (map 'string (e-helper n) str)) 

; 2) Program that decrypts a word given as a string (first argument) using Caesar's cipher in the modulo given (second argument)

; Rotates the encrypted chars back to their original meaning

(defun rotate-back (char n)
  (elt *alphabet* (mod (- (alphabet-index char) n)(length *alphabet*))))

; Makes the rotate-back function accesable for map

(defun d-helper (n)
  #'(lambda (char)
      (rotate-back char n)))

; Returns the decrypted string

(defun decrypt (str n)
  (map 'string (d-helper n) str))
For some reason Lisp gives me an error message when I try a sentence instead of a single word, is there something wrong with my parameter? The given error is:
+: NIL is not a number
[Condition of type SIMPLE-TYPE-ERROR]

Some idea what went wrong?

sinnatagg
Posts: 29
Joined: Tue Apr 21, 2009 3:04 am

Re: Newbie needs help please

Post by sinnatagg » Tue Nov 03, 2009 12:46 pm

RaydPanda wrote: For some reason Lisp gives me an error message when I try a sentence instead of a single word, is there something wrong with my parameter? The given error is:
+: NIL is not a number
[Condition of type SIMPLE-TYPE-ERROR]

Some idea what went wrong?
Your alphabet-index() function probably returns nil, while the +() function expects numbers as its arguments. I'ld guess your sentence contains upper case characters which can't be looked up in the *alphabet* string, and position then returns nil.

-a

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

Re: Newbie needs help please

Post by ramarren » Tue Nov 03, 2009 1:01 pm

What sinnatagg said. To make it more explicit you could change
RaydPanda wrote:

Code: Select all

(defun alphabet-index (char)
  (position char *alphabet*))
to

Code: Select all

(defun alphabet-index (char)
  (or (position char *alphabet*)
      (error "There is no '~a' in alphabet." char)))
which will trigger a more descriptive error.

RaydPanda
Posts: 8
Joined: Thu Oct 22, 2009 11:13 am
Location: Bergen, Norway

Re: Newbie needs help please

Post by RaydPanda » Wed Nov 04, 2009 3:41 am

Ah, I didn't realize that the uppercase letter was the problem, I thought the white space caused the error. Now I got the problem fixed by simply changing my *alphabet* to:

Code: Select all

(defparameter *alphabet* " aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ,.0123456789")
Which gives me the following results:

Code: Select all

CL-USER> (encrypt "This is a sentence" 3)
"vIJTbJTbBbTFOUFODF"
CL-USER> (decrypt "vIJTbJTbBbTFOUFODF" 3)
"This is a sentence"
Problem solved and first part of the project finished, I'm so happy as you wouldn't believe.
Thanks again for all the help!

Now I have to enhance the program so that it can also use Vigenère cipher. Let's see how that works:

I know that I can express Vigenere algrebraically as Ciphertextletter index = Plaintextletter index + Keywordletter index (mod length of the used alphabet)

SO what I need to do is write some functions that match the length of the plaintext and keyword (by repeating the keyword), then look up the index of the equaling characters in the two string, add them together and feed them to an altered rotate function.

So much for the theory... the programing itself is a bit harder then the simple logic behind it, unfortunately. But I'm sure that I can get it right with your help :D

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

Re: Newbie needs help please

Post by ramarren » Wed Nov 04, 2009 5:08 am

RaydPanda wrote:So much for the theory... the programing itself is a bit harder then the simple logic behind it, unfortunately. But I'm sure that I can get it right with your help :D
Unfortunately this cannot be done using just mapping (at least without producing the index range as a list, which is silly), so you might want to read the LOOP chapter in PCL if you have not already. You can't collect from LOOP directly into the string, but that is what COERCE is for.

Personally I don't really like LOOP, preferring ITERATE, and this particular problem has pretty nice SERIES solution actually, but I suppose one would like to avoid dependencies for homework. Sometimes I wish SERIES was included in the standard, instead of ending as just an appendix to CommonList The Language 2ed.

RaydPanda
Posts: 8
Joined: Thu Oct 22, 2009 11:13 am
Location: Bergen, Norway

Re: Newbie needs help please

Post by RaydPanda » Fri Nov 06, 2009 6:28 am

Hm, I don't seem to get it right... what I managed until now is a function that actually encrypts a character with a key character. I need to find a possibility to use that function on two strings instead of two characters. The functions so far look like this:

Code: Select all

; 1) Program that takes in a word(given as a string) plus a number and encrypts the word using Caesar's Cipher in the modulo given as the number:

; Alphabet as parameter plus one whitespace at index 0

(defparameter *alphabet* " aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ,0.1?2!3;4:56789")

; Finds the index of char in  *alphabet*:

(defun alphabet-index (char)
  (position char *alphabet*))
 
; Finds the char shifted n places to the right in the alphabet:

(defun rotate (char n)
  (elt *alphabet* (mod (+ (alphabet-index char) n)(length *alphabet*))))

; makes the rotate function accesable for map

(defun e-helper (n)
  #'(lambda (char)
      (rotate char n)))


; Returns the encrypted string
 
(defun encrypt (str &optional (n 7))
  (map 'string (e-helper n) str)) 

; 2) Program that decrypts a word given as a string (first argument) using Caesar's cipher in the modulo given (second argument)

; Rotates the encrypted chars back to their original meaning

(defun rotate-back (char n)
  (elt *alphabet* (mod (- (alphabet-index char) n)(length *alphabet*))))

; Makes the rotate-back function accesable for map

(defun d-helper (n)
  #'(lambda (char)
      (rotate-back char n)))

; Returns the decrypted string

(defun decrypt (str &optional (n 7))
  (map 'string (d-helper n) str))

; 3) Program that takes a string and a keyword and encrypts the string using Vigenere cipher

; C(iphertext)index= P(laintext)index+K(ey)index (mod alphabetlength)

; Function that looks up index of equaling characters in plaintextstring and repeated keywordstring and adds them together

(defun add-indexes (P K)
  (+ (alphabet-index P) (alphabet-index K))))
  
; Function that rotates the characters of the plaintext modulo the calculated shift from add-indexes

(defun v-rotate (P K)
  (rotate P (add-indexes P K))))
I looked through the loop descriptions but didn't find something that struck me as the right one. I started testing out loop for i from 0 (length plaintext) and loop across 'string... but didn't get anywhere near what I need.
May I have some more hints?

P.S. I may not use iterate since it is not in the standard, sorry.

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

Re: Newbie needs help please

Post by ramarren » Fri Nov 06, 2009 7:11 am

Some of your parentheses seem unbalanced.
RaydPanda wrote:I looked through the loop descriptions but didn't find something that struck me as the right one. I started testing out loop for i from 0 (length plaintext) and loop across 'string... but didn't get anywhere near what I need.
May I have some more hints?
I don't know how to give a hint, because, at least in retrospect this is really obvious, and I don't like LOOP anyway so I wouldn't want to inflict careful reading of the specification on someone, so:

Code: Select all

(defun vigenere (string key)
  (coerce (loop with length-string = (length string)
                with length-key = (length key)
                for i from 0 below length-string
                collect (v-rotate (char string i) (char key (mod i length-key))))
          'string))
For comparision, ITERATE solution:

Code: Select all

(defun vigenere-iterate (string key)
  (coerce (iter (with length-key = (length key))
                (for c in-string string with-index i)
                (collect (v-rotate c (char key (mod i length-key)))))
          'string))
SERIES solution, unfortunately cluttered by an utility function:

Code: Select all

(defun scan-string-loop (string)
  (declare (optimizable-series-function 2))
  (let ((length (length string)))
    (scan-fn '(values character (integer 0 #.array-dimension-limit))
             #'(lambda ()
                 (values (char string 0) 0))
             #'(lambda (char index)
                 (declare (ignore char))
                 (let ((new-index (if (= (1+ index) length)
                                      0
                                      (1+ index))))
                   (values (char string new-index) new-index))))))

(defun vigenere-series (string key)
  (series:collect 'string
    (mapping ((c (scan 'string string))
              (k (scan-string-loop key)))
             (v-rotate c k))))

nuntius
Posts: 538
Joined: Sat Aug 09, 2008 10:44 am
Location: Newton, MA

Re: Newbie needs help please

Post by nuntius » Fri Nov 06, 2009 10:29 am

I often skip the loop languages and just use the straightforward macros. Here's a reduced consing solution (not tested).

Code: Select all

(defun vigenere (string key)
  (let* ((length-string (length string))
         (length-key (length key))
         (result (make-string length-string)))
    (dotimes (i length-string)
      (setf (char result i)
            (v-rotate (char string i) (char key (mod i length-key)))))
    result))

RaydPanda
Posts: 8
Joined: Thu Oct 22, 2009 11:13 am
Location: Bergen, Norway

Re: Newbie needs help please

Post by RaydPanda » Sun Nov 08, 2009 1:40 pm

:shock:

You guys are unbelievable! Thanks a lot! And yes, it really seems not this hard in retrospect, but that is nearly always the case. ;)

Now I only have to find out how to make the matching decryption and I'm done for good. Unfortunately something along the following lines doesn't work since it simply produces a string of the repeated keyword:

Code: Select all

; P(laintext)index = C(ipehertext)index -  K(ey)index (mod alphabetlength) => Decryption


; Function that looks up the index of a ciphertext character and it's equaling key character and subtracts them from each other

(defun substract-indexes (C K)
  (- (alphabet-index C) (alphabet-index K))))

; Function that rotates the Ciphertexts char back to its Plaintext position

(defun v-rotate-back (C K)
  (rotate-back C (substract-indexes C K)))

; returns decrypted string

(defun v-decrypt( (string key)
  (coerce (loop with length-string = (length string)
                with length-key = (length key)
                for i from 0 below length-string
                collect (v-rotate-back (char string i) (char key (mod i length-key))))
          'string))
So back into the loop fun, or maybe the problem lies within the rotation. I'm not sure if the loop can take in negative numbers. I'll see if I can figure it out. Maybe I'll get it right.

Thanks again for your help! All of you are making this project interesting for me again, and I really learn a lot by looking at your code and seeing the different possibilities.

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

Re: Newbie needs help please

Post by ramarren » Sun Nov 08, 2009 3:05 pm

I haven't noticed it before, but you are rotating wrong, since you computer the sum/difference of indexes, and then add them to character index again in rotate/rotate-back. Either drop the add/subtract-indexes and just do:

Code: Select all

(defun v-rotate (P K)
  (rotate P (alphabet-index K)))

(defun v-rotate-back (C K)
  (rotate-back C (alphabet-index K)))
Or don't rotate, just return character at computed index.

Post Reply