簡単移調ツールをつくってみました。

id:ha-tan:20051003:1128289452でつくったRuby版移調ツールをHaskellに移植してみました。
ファイル: icho.hs

import System
import Control.Monad
import Data.Char
import Data.Maybe
import Data.List

usage :: IO ()
usage = do putStrLn "usage: icho [from] [to]"
           exitFailure

main :: IO ()
main = do
  args <- getArgs
  when (length args < 2) $ usage
  let fromIndex = noteIndex $ canonNote $ head args
      toIndex   = noteIndex $ canonNote $ head $ tail args
  when (fromIndex < 0 || toIndex < 0) $ usage
  icho fromIndex toIndex
  where
    icho :: Int -> Int -> IO ()
    icho fromIndex toIndex = do
      let rotateNotes = rotate (toIndex - fromIndex) notes
      mapM_ printNote $ zip notes rotateNotes

    printNote :: (String, String) -> IO ()
    printNote (from, to) =
      putStrLn $ (ljust 2 from) ++ " => " ++ (ljust 2 to)

    ljust :: Int -> String -> String
    ljust width s = s ++ replicate (width - (length s)) ' '

notes :: [String]
notes =  [ "C", "C#", "D", "Eb", "E", "F",
           "F#", "G", "G#", "A", "Bb", "B" ]

canonNote :: String -> String
canonNote [] = error "internal error at canonNote."
canonNote (s:ss) = [toUpper s] ++ ss

noteIndex :: String -> Int
noteIndex note = case elemIndex note notes of
                   Just i  -> i
                   Nothing -> -1

rotate :: Int -> [a] -> [a]
rotate i ss = 
  let i' = if i > 0 then i else length ss + i
      (s1, s2) = splitAt i' ss
  in s2 ++ s1

以下使用例です。GからCに移調する場合。

$ ./icho.exe g c
C  => F
C# => F#
D  => G
Eb => G#
E  => A
F  => Bb
F# => B
G  => C
G# => C#
A  => D
Bb => Eb
B  => E