Perl入学式 第2回までの範囲(+α)でROT13
ROT13 というのは、簡単な暗号の一つです。
暗号化した文字列 uryyb jbeyq
を以下の表をもとに置換すると、hello world
という文字列になります。
変換前 | 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 |
変換前 | n | o | p | q | r | s | t | u | v | w | x | y | z |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
変換後 | a | b | c | d | e | f | g | h | i | j | k | l | m |
名前の通り、 a
は 13文字後の n
に置換されており、他の文字も同様です。13文字後が z
以降になる場合には、a
に戻ります。
さて、これを Perl入学式の第2回までの範囲でやってみましょう!
Perl入学式の第2回は四則演算、配列、配列操作の関数、forを使った繰り返しまで、でした。
書いてみたものがこちらです。
#!/usr/bin/env perl use strict; use warnings; use feature qw/say/; my $secret_str = 'uryyb jbeyq'; # 暗号化済みの文字列 my @secret_str = split "", $secret_str; # 暗号化文字列を1文字ずつ配列に格納 my @alphabet = ( 'a' .. 'z' ); # アルファベットが1文字ずつ格納された配列 # 暗号化した文字列を1文字ずつ処理する。 for my $char (@secret_str) { if ( $char eq ' ' ) { # 文字ではなく、スペースだったらそのまま表示 print $char; } else { my $i = 0; # ループが何回目かを保存する変数 my $index = 0; # アルファベットの何文字目かを保存する変数 # @alphabetの添え字と同じ for my $c (@alphabet) { #アルファベットを1文字ずつ取り出して比較する if ( $char eq $c ) { # 文字列なので比較は eq $index = $i; # 合致したら $indexに何番目のアルファベットだったか保存 } else { $i++; # 合致しなかったら、$indexに1加える } } my $recover_index = $index + 13; # 今回の暗号は13文字ずらしたことがわかっているので、 # 13文字進めたものを本来の文字列の添字とする if ( $recover_index > 26 ) { # 13文字分添字を足したら、アルファベットの文字数を超えた場合 print $alphabet[ $recover_index % 26 ]; # 剰余で26を超えた分だけ添字にする } else { print $alphabet[$recover_index]; # 添字をそのまま使う } } }
ううむ、書けなくはないのですが、ちょっと長いですね・・・でも書けないことはないです!
last
でforをぬけてみる。
さて、ここから第2回の範囲を超えていくとどうなるのか?というのをやっていきます。
for 文や while 文などの繰り返しの中で、途中で抜けたいときに使う last
を使います。ちょっとだけ短くなりました。変数も1つ($i
)消えました。
#!/usr/bin/env perl use strict; use warnings; use feature qw/say/; my $secret_str = 'uryyb jbeyq'; my @secret_str = split "", $secret_str; my @alphabet = ( 'a' .. 'z' ); for my $char (@secret_str) { if ( $char eq ' ' ) { print $char; } else { my $index = 0; for my $c (@alphabet) { if ( $char eq $c ) { last; # 文字が合致した場合には、そこで抜ける } else { $index++; } } my $recover_index = $index + 13; if ( $recover_index > 26 ) { print $alphabet[ $recover_index % 26 ]; } else { print $alphabet[$recover_index]; } } }
index
関数で、何文字目かを文字列から取ってみる
さらに超えていきます。Perlの index
関数は以下のように用います。文字列から、特定の文字が何番目にあるかを返す関数です。
#!/usr/bin/env perl use strict; use warnings; my $alphabet = 'abcdefghijklmnopqrstuvwxyz'; my $index = index $alphabet, 'a'; print $index . "\n"; # 0 $index = index $alphabet, 'n'; print $index . "\n"; # 13 $index = index $alphabet, 'z'; print $index . "\n"; # 25
この index
関数があれば、アルファベットの添え字が何番目かを調べるのにfor
を使う必要がなくなりますね!
for文 の中にある for文(入れ子のfor文) が消えてすっきりしました。
#!/usr/bin/env perl use strict; use warnings; use feature qw/say/; my $secret_str = 'uryyb jbeyq'; my @secret_str = split "", $secret_str; my @alphabet = ( 'a' .. 'z' ); # index関数を使うため、アルファベットを格納したスカラー変数を用意 my $alphabet_str = join "", @alphabet; for my $char (@secret_str) { if ( $char eq ' ' ) { print $char; } else { my $index = index $alphabet_str, $char; # index関数で何文字目かを調べる my $recover_index = $index + 13; if ( $recover_index > 26 ) { print $alphabet[ $recover_index % 26 ]; } else { print $alphabet[$recover_index]; } } }
三項演算子を使う
まだ、短くすることはできるのでしょうか?もちろん可能です。ただ、「第2回までの範囲をそれほど逸脱しない」となると難しいですね・・・次で最後とします。
#!/usr/bin/env perl use strict; use warnings; use feature qw/say/; my $secret_str = 'uryyb jbeyq'; my @secret_str = split "", $secret_str; my @alphabet = ( 'a' .. 'z' ); # index関数を使うため、アルファベットを格納したスカラー変数を用意 my $alphabet_str = join "", @alphabet; for my $char (@secret_str) { if ( $char eq ' ' ) { print $char; } else { my $index = index $alphabet_str, $char; # index関数で何文字目かを調べる my $recover_index = $index + 13; # 三項演算子 $recover_index = $recover_index > 26 ? $recover_index % 26 : $recover_index; print $alphabet[$recover_index]; } }
添字が26より大きかった場合を処理していたif文が消えています。
これは三項演算子を用いた条件分岐の書き方です。三項演算子はPerlだけではなく、C言語やJava, PHPにもあります。
Perl入学式の第2回までの範囲から、last
, index
, 三項演算子を用い他スクリプトを書いてみました。
同じ結果をもたらすスクリプトでも、行数や変数の数が大きく変わってきます。
Perl入学式の範囲から越境して、いろいろな関数を触ってみてください!
Perl入学式第3回までの範囲だと・・・?
2020年01月25日はPerl入学式 in東京の第3回です。
perl-entrance-tokyo.connpass.com
ハッシュと正規表現を使うことで、ROT13は更にわかりやすくなり、更に短くすることも可能です。
(多分)講義では取り上げませんが、このブログでは第3回の範囲でROT13を書いてみたいと思っています。