goibhniu has asked for the wisdom of the Perl Monks concerning the following question:

In my crypto hobby we create Ciphertext to plaintext alphabets. I've written the below to feature the key subroutine. I'm wondering if the foreach loops can be adapted to map functions or whether the if (not exists . . . invalidates map for use in this case.

#!/usr/bin/perl -w use strict; use warnings; my %p2C; my %C2p; sub key { my $kp= shift; my $caesar= shift; my $a=0; my $k; $p2C{' '}=' '; $C2p{' '}=' '; foreach (split(//, $kp)) { if (not exists $p2C{$_}) { #print $_; $k=chr(ord('A')+(($a++)+$caesar)%26); $p2C{$_}=$k; $C2p{$k}=$_; } } foreach ('a'..'z') { if (not exists $p2C{$_}) { $k=chr(ord('A')+(($a++)+$caesar)%26); $p2C{$_}=$k; $C2p{$k}=$_; } } } sub printKeys { my $href = shift; print "\nCT: "; print(($_).' ') foreach (sort keys %{$href}); print "\npt: "; print(($$href{$_}).' ') foreach (sort keys %{$href}); print "\n"; } key ('thequickbrownfoxjumpsoverthelazydogs',10); printKeys(\%p2C); printKeys(\%C2p); print "----------------------------------------------------------\n"; undef %p2C; undef %C2p; key ('thequickbrown',10); printKeys(\%p2C); printKeys(\%C2p);

produces the following:

C:\chas_sandbox>keyonly.pl CT: a b c d e f g h i j k l m n o p q r s t u v w x y z pt: F S Q I M X J L P Z R E A W U B N T C K O D V Y H G CT: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z pt: m p s v l a z y d g t h e q u i c k b r o w n f x j ---------------------------------------------------------- CT: a b c d e f g h i j k l m n o p q r s t u v w x y z pt: X S Q Y M Z A L P B R C D W U E N T F K O G V H I J CT: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z pt: g j l m p s v x y z t h e q u i c k b r o w n a d f C:\chas_sandbox>


I humbly seek wisdom.

Replies are listed 'Best First'.
Re: adapting foreach to map
by dogz007 (Scribe) on Aug 17, 2007 at 04:09 UTC
    A map will not help much here, since you don't need the array that it would return. However, I did notice that the code inside your two foreach loops is identical, suggesting that they should be combined as below. In addition, rather than calculate the uppercase ciphertext letters on the fly, I loaded up a range of letters, scrolled it around the correct number of times, and then shifted it off as needed. I also weeded out the undesirable spaces and forced lowercase key input in that first line of sub key. Same results, but I think it's easier to understand.

    use strict; my %p2C; my %C2p; key ('thequickbrownfoxjumpsoverthelazydogs',10); printKeys(\%p2C); printKeys(\%C2p); sub printKeys { my $href = shift; print "\nCT: "; print(($_).' ') foreach (sort keys %{$href}); print "\npt: "; print(($$href{$_}).' ') foreach (sort keys %{$href}); print "\n"; } sub key { my @kp = grep /[a-z]/, split //, shift; my $caesar= shift; my @alpha = ('A'..'Z'); push @alpha, shift @alpha for (1..$caesar); foreach (@kp,'a'..'z') { if (not exists $p2C{$_}) { my $k = shift @alpha; $p2C{$_}=$k; $C2p{$k}=$_; } } }

    Prints:

    CT: a b c d e f g h i j k l m n o p q r s t u v w x y z pt: F S Q I M X J L P Z R E A W U B N T C K O D V Y H G CT: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z pt: m p s v l a z y d g t h e q u i c k b r o w n f x j

      Wonderful. That actually was bothering me, but I knew the inputs to the loops were different and hadn't worked out that I could just concatenate as two lists into one list.

      Thanks alot.


      I humbly seek wisdom.
Re: adapting foreach to map (why?)
by tye (Sage) on Aug 17, 2007 at 03:27 UTC

    map is for returning a list (computed based on an input list); you aren't doing that so map is inappropriate. But you can use braces to pass a block to map so you could (mis)use it here. Did you try anything?

    - tye        

      I haven't tried anything on this one. It's a wierd kind of brain lock. I wrote this:

      %{$matchSolutionref} = map { $CTchars[$_] => (split(//,$word))[$_] } (0..$#CTchars);

      in this code and liked how it turned out. I keep trying to write something like:

      %p2C = map {if (not exists $p2C{$_}) {$k=chr(ord('A')+(($a++)+$caesar) +%26); $k => $_}} (split(//, $kp))

      but my fingers won't let me. Or maybe my fingers keep trying to write that and my brain won't let me.

      I guess I'm enamoured of the map BLOCK LIST syntax and am trying to get away with too much code in the BLOCK part.


      I humbly seek wisdom.

      OK, I'm trying things. Here's what I've got.

      This first one is the nominal - it works and accomplishes what I want. I've decluttered from above so I'm only dealing with one hash and not using a temporary variable for the big, modular math. I've added a nice map statement to turn %p2C inside out, becoming %C2p.

      This produces output thus:

      C:\chas_sandbox>foreachasmap.pl CT: a b c d e f g h i j k l m n o p q r s t u v w x y z pt: F S Q I M X J L P Z R E A W U B N T C K O D V Y H G CT: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z pt: m p s v l a z y d g t h e q u i c k b r o w n f x j C:\chas_sandbox>

      Next, I've turned the if statement into a ( ? : ) operator. In version 1 I was looking for if (not exists . . .) so I needed something to do if it did exists and assigned to a hash key that I knew I wouldn't use, '#'. I've spotted my bug here - the autoincrement of $a happens every time now instead of only if (not exists . . .). Any suggestions for that or am I a victim of my own ambition?

      Here's the second output.

      C:\chas_sandbox>foreachasmap.pl CT: # a b c d e f g h i j k l m n o p q r s t u v w x y z pt: T N S Q Q M X S L P A R M C W U D N T E K O G V Z P O CT: A C D E G K L M N O P Q R S T U V W X Z pt: j m p s v t h l q z i c k b # o w n f x

      The last version here would be what I'm really after. I'm just moving things around according to the doc for map:

      %hash = map { getkey($_) => $_ } @array;
      is just a funny way to write
      %hash = (); foreach $_ (@array) { $hash{getkey($_)} = $_; }

      I have a foreach, a list and I'm mapping a key to a value in a hash, so it seems it should be no more complicated than this. Here's the code:

      I don't even get the same output as in version 2:

      C:\chas_sandbox>foreachasmap.pl CT: a b c d e f g h i j k l m n o p q r s t u v w x y z pt: U V W X Y Z A B C D E F G H I J K L M N O P Q R S T CT: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z pt: g h i j k l m n o p q r s t u v w x y z a b c d e f C:\chas_sandbox>

      I know this may all seem academic since it's working in version 1, but, well, it's academic and I'd like to learn something.


      I humbly seek wisdom.