by shigemk2

当面は技術的なことしか書かない

rot13をScalaで書く

rot13は、シーザー暗号の発展版みたいなもので、abcdeという文字列が合った場合、13文字ずらしてnopqrという文字列を出力する素敵な暗号のこと。実装方法自体はココにあるんです。しかもHaskellとScala。ちょうどいい。

langref.org - scala and haskell - Strings | Manipulation | Simple substitution cipher

イカ引用。13文字バージョンだけではなく、47文字ずらすバージョンも実装してくれてる。

val uppers = 'A' to 'Z'
val lowers = 'a' to 'z'

val alpha13 = (uppers ++ lowers).mkString
val beta13 = ((uppers drop 13) ++ (uppers take 13) ++ (lowers drop 13) ++ (lowers take 13)).mkString

val alpha47 = """!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"""
val beta47 = """PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO"""

// generic translation function
def rot (alpha: String, beta: String)(c: Char) = if (alpha contains c) beta(alpha indexOf c) else c

// specific translation functions curried with the respective alphabets
val rot13 = rot(alpha13, beta13) _
val rot47 = rot(alpha47, beta47) _

assert(("Hello World #123" map rot13).toString == "Uryyb Jbeyq #123")
assert(("Hello World #123" map rot47).toString == "w6==@ (@C=5 R`ab")
import Char

ebg13 c | isAlpha c && toLower c <= 'm' = chr ((ord c) + 13)
| isAlpha c && toLower c > 'm' = chr ((ord c) - 13)
| otherwise = c
rot13 str = map ebg13 str

ebg47 c | c > ' ' && c <= 'N' = chr ((ord c) + 47)
| c > 'N' && c <= '~' = chr ((ord c) - 47)
| otherwise = c
rot47 str = map ebg47 str

見たら分かると思うのですが、実装方法がScalaとHaskellではぜんぜん違う。というわけで、とりあえず、うえのHaskellの例を直訳してみる。直訳といっても、HaskellのisALphaに相当する関数がScalaにはないようなので、そこは正規表現でごまかしてみる。

原理としては、Char値を数値に変換、演算して、さらにtoCharで変換する、というHaskellプログラムの流れを踏襲してる。

def alpha13(c: Char): Char = c match {
  case c if c.toString.matches("[A-Za-z]") && c.toLower <= 'm' => (c.toInt + 13).toChar
  case c if c.toString.matches("[A-Za-z]") && c.toLower >= 'm' => (c.toInt - 13).toChar
  case _ => c
}

def rot13(str: String): String = {
  str.map(c => alpha13(c))
}

println(rot13("abcde")) // nopqr
println(rot13("I am shigemk2")) // V nz fuvtrzx2
println(rot13("z")) // m

ScalaでChar値をごにょごにょ

こんなかんじ。Scala 2.11.7 比較とかが出来る。数字に変換して足してまたCharに戻すなども出来る。

scala> 'c'.toLower
res14: Char = c

scala> 'c'.toLower < 'm'
res15: Boolean = true

scala> 'c'.toLower < 'b'
res16: Boolean = false

scala> 'c'.toInt
res17: Int = 99

scala> ('c'.toInt - 13)
res18: Int = 86

scala> ('c'.toInt - 13).toChar
res19: Char = V

scala> ('c'.toInt + 13).toChar
res20: Char = p

www.atmarkit.co.jp

Char同士の比較については、APIに書いてあったりする。

www.scala-lang.org