マシンの観点から見ると、ローターは次の 2 つのものです。
- シンボルから別のシンボルへの関数です
- それは別の機能になるために前進することができます
次のような型を書くことができます。
newtype Symbol = Symbol {representation :: Int}
data Rotor = Rotor {
transformation :: Symbol -> Symbol,
next :: Rotor
}
マシンが反射から対称性について知っている場合、それは次のようなものかもしれません
data Rotor = Rotor {
forward :: Symbol -> Symbol,
backward :: Symbol -> Symbol,
next :: Rotor
}
(次のようなものを使用することもできます[(Symbol -> Symbol,Symbol -> Symbol)]
)
では、どのように を構築しRotor
ますか? ローター IC の例の定義を見てみましょう。
rotorICdefinition = map symbol $ "DMTWSILRUYQNKFEJCAZBPGXOHV"
ここで、シンボルには type が必要Char -> Symbol
です。このようなことをする必要があります。
symbol :: Char -> Symbol
symbol x = Symbol $ ord x - ord 'A'
魔法の束がありord
ます。アルファベットがどの順序で来るかはすでにわかっています。例えば:
Prelude Data.Char> map ord "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
[65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90]
次に、その定義からローターを作成できるようにしたいと思います
rotorIC :: Rotor
rotorIC = makeRotor rotorICdefinition
そのmakeRotor
ため、タイプが必要です
makeRotor :: [Symbol] -> Rotor
次のように定義できます
makeRotor definition = makeRotor' 0
where
makeRotor' steps = Rotor {
forward = forwardLookup steps,
backward = reverseLookup steps,
next = makeRotor' ((steps+1) `mod` symbolModulus)
}
forwardLookupTable = array (minBound, maxBound) (zip symbols definition)
reverseLookupTable = array (minBound, maxBound) (zip definition symbols)
forwardLookup = lookup forwardLookupTable
reverseLookup = lookup reverseLookupTable
lookup lookupTable steps = (lookupTable !) . Symbol . (`mod` symbolModulus) . (+ steps) . representation
ここでは多くのことが起こっています。ローターの無限のストリームを作成しています。各ローターは前のローターから 1 ステップ回転し、0 ステップ回転したローターから始めます。steps
では、makeRotor'
回転したステップ数を追跡しています。はRotor
と の両方forward
で構成されbackward
、ローターが回転したステップ数を考慮に入れます。ローターは同じですが、もうnext
一段回転しています。最終的に整数がオーバーフローしないようにするためにmod
、存在するシンボルの数を法としsymbolModulus
ます。(それを行うより効率的な方法があります)。2 つのルックアップは、一度構築されたルックアップ テーブルに基づいており、範囲内のすべてのシンボル(minBound, maxBound)
をdefinition
. のlookup
それ自体は、入力を取り、ステップ数を追加し、そのモジュラスにシンボル数を取り、ルックアップ テーブルのその位置にあるものを返すだけです。
minBound
これには、新たに出現する、maxBound
、symbols
、およびを定義する必要がありsymbolModulus
ます。
instance Bounded (Symbol) where
minBound = symbol 'A'
maxBound = symbol 'Z'
symbolModulus = (representation maxBound) - (representation minBound) + 1
-- This could have some other definition
symbols = map symbol $ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
少し UI を追加して、プログラム全体をまとめます。
module Main (
main
) where
import Data.Char
import Data.Array -- requires array package
import System.IO
main = go rotorIC
where go rotor = do
putStr "Input : "
hFlush stdout
command <- getLine
case command of
"next" -> go (next rotor)
[] -> return ()
text -> case all (inRange (char minBound, char maxBound)) text of
True -> do
putStrLn . ("Forward : " ++) $ map (char . forward rotor . symbol) text
putStrLn . ("Backward: " ++)$ map (char . backward rotor . symbol) text
go rotor
_ -> do
putStrLn "Not all of the input was symbols"
go rotor
newtype Symbol = Symbol {representation :: Int} deriving (Eq, Ord, Ix)
symbol :: Char -> Symbol
symbol x = Symbol $ ord x - ord 'A'
char :: Symbol -> Char
char x = chr $ representation x + ord 'A'
instance Bounded (Symbol) where
minBound = symbol 'A'
maxBound = symbol 'Z'
symbolModulus = (representation maxBound) - (representation minBound) + 1
-- This could have some other definition
symbols = map symbol $ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
data Rotor = Rotor {
forward :: Symbol -> Symbol,
backward :: Symbol -> Symbol,
next :: Rotor
}
rotorICdefinition = map symbol $ "DMTWSILRUYQNKFEJCAZBPGXOHV"
rotorIC :: Rotor
rotorIC = makeRotor rotorICdefinition
makeRotor :: [Symbol] -> Rotor
makeRotor definition = makeRotor' 0
where
makeRotor' steps = Rotor {
forward = forwardLookup steps,
backward = reverseLookup steps,
next = makeRotor' ((steps+1) `mod` symbolModulus)
}
forwardLookupTable = array (minBound, maxBound) (zip symbols definition)
reverseLookupTable = array (minBound, maxBound) (zip definition symbols)
forwardLookup = lookup forwardLookupTable
reverseLookup = lookup reverseLookupTable
lookup lookupTable steps = (lookupTable !) . Symbol . (`mod` symbolModulus) . (+ steps) . representation
ここで、いくつかの例を見てみましょう。アルファベットの最初の 6 文字の順方向変換が の始まりですrotorICdefinition
。
Input : ABCDEFG
Forward : DMTWSIL
Backward: RTQAONV
の先頭に入れるとrotorICdefinition
、後方変換としてアルファベットの最初の 6 文字が返されます。
Input : DMTWSIL
Forward : WKBXZUN
Backward: ABCDEFG
ローターの次のステップに進むと、非常に異なる結果が得られます。
Input : next
Input : ABCDEFG
Forward : MTWSILR
Backward: TQAONVY
しかし、'A' の 1 つ前に文字を入れると、再び定義が返されます。
Input : ZABCDEF
Forward : DMTWSIL
Backward: RTQAONV
ローターの次のステップにさらに 25 回進むと、最初の場所に戻ります。
Input : next
(25 times total)
Input : next
Input : ABCDEFG
Forward : DMTWSIL
Backward: RTQAONV