数値をASCIIを用いてaからzまで(半角英字)の文字列に変換

何をしたいのか

業務で書いたコードなのだが、作成する元になったアイディアがボツになったため、お蔵入り。需要は無いだろうけど、せっかくなので当時、ググっても出てこなかったので公開しておこうと思う。1から始まる任意の数字をASCIIで定義されている数値と文字の紐付け表からaからzの適切な小文字アルファベット(半角英字)に変換するという処理だ。forで生成されるiterationのカウントを使ってa-zの文字列を生成するために使用していた。言葉で伝えても分かりづらいので動作の例を下記に記す

www.asciitable.com

i = 1 -> a
i = 2 -> b
:
i = 26 -> z
i = 27 -> a
i = 28 -> b
:
:

業務ではgolangを使って記述したのだが、せっかくなのでElixirで書き直した

予備知識

アルファベットとはご存知の通り、a-zで合計26文字が定義されている。またASCIIでは小文字のa97、小文字のz122として定義している。つまりは、26回周期でまた同じ半角英字が現れるということになる

アルファベット番号 ASCII番号
a 97
b 98
z 122

実際のコード

golang

// 数値をasillに変換する関数(eg: 97 -> a, 122 -> z)
func IntToASCII(num int) int {
    // 26 -> アルファベットの数
    if num > 26 {
        div := num / 26
        // 割り切れる際に整合性を取るため-1
        if num%26 == 0 {
            div -= 1
        }
        num -= 26 * div
    }
    // aが97で始まるため+97
    return num + 96
}

実行結果

package main

import (
    "fmt"
)

func main() {
    for i := 1; i < 100; i++ {
        res := IntToASCII(i)
        fmt.Println("i = ", i, ", res = ", res, ", str = ", string(res))
    }
}
i =  1 , res =  97 , str =  a
i =  2 , res =  98 , str =  b
i =  3 , res =  99 , str =  c
:
i =  24 , res =  120 , str =  x
i =  25 , res =  121 , str =  y
i =  26 , res =  122 , str =  z
i =  27 , res =  97 , str =  a
i =  28 , res =  98 , str =  b
:
i =  51 , res =  121 , str =  y
i =  52 , res =  122 , str =  z
i =  53 , res =  97 , str =  a
i =  54 , res =  98 , str =  b
:
i =  95 , res =  113 , str =  q
i =  96 , res =  114 , str =  r
i =  97 , res =  115 , str =  s
i =  98 , res =  116 , str =  t
i =  99 , res =  117 , str =  u

全文のコード詳細な結果はこちら
play.golang.org

Elixir

ゴリ押し感はいなめない。こういう計算処理を関数型でゴリゴリ書こうとするとどうなるんだろうと考えさせられた

# 無名関数を定義
int_to_ASCII = fn num -> 
  if num > 26 do
    num - (div(num, 26) + if(rem(num, 26)==0, do: -1, else: 0)) * 26 + 96
  else
    num + 96
  end
end

実行結果

Enum.each(1..100, fn n -> 
  res = int_to_ASCII.(n)
  IO.puts("i = #{n}, res = #{res}, str = #{[res]}")
end)
i = 1, res = 97, str = a
i = 2, res = 98, str = b
i = 3, res = 99, str = c
i = 4, res = 100, str = d
:
:
i = 97, res = 115, str = s
i = 98, res = 116, str = t
i = 99, res = 117, str = u
i = 100, res = 118, str = v

無名関数を使わないver
こっちの方がElixirっぽいかな

defmodule ASCII do
  def from_int(num) when num > 26 do
    num - calc_helper(num) * 26 + 96
  end
  def from_int(num), do: num + 96
  def calc_helper(num) do
    if rem(num, 26) == 0 do
      div(num, 26) -1
    else
      div(num, 26)
    end
  end
end

おまけ

Elixirのコードを後日さらにコンパクトに改良

defmodule ASCII do
  def from_int(num) when num > 26 do
    num - calc_helper(num) * 26 + 96
  end
  def from_int(num), do: num + 96
  def calc_helper(num) when rem(num, 26) == 0, do: div(num, 26) -1
  def calc_helper(num), do: div(num, 26)
end

参考文献