SimpleBoxes

Re: Excel列名変換問題で第2回社内プログラミングコンテストを開催してみた(前編)

JunichiIto さんによる「Excel列名変換問題で第2回社内プログラミングコンテストを開催してみた(前編)」という記事を見かけて自分でもやってみる。

正確に時間を測ったわけではありませんが、問題 1 と問題 2 あわせて一応 90 分で解けたと思います。

問題1: Excel列名変換問題
  • 仕様
    • 入力されたアルファベットを数字に変換する。
    • 変換ルールはExcelの列名と同等。
    • 例) A=1、B=2、Z=26、AA=27、XFD=16384
  • 起動時引数
    • [0] アルファベット (A〜ZZZZ...[上限なし])
  • 実行例
    • ExcelColConv.pl A → 1
    • ExcelColConv.pl AA → 27

[→Excel列名変換問題で第2回社内プログラミングコンテストを開催してみた(前編)]

問題 1 に対する私の回答

問題2: Excel列名変換問題(逆変換)
  • 仕様
    • 入力された数字をアルファベットに変換する。
    • ただし、問題1で作ったプログラムを拡張すること。
  • 起動時引数
    • [0] 0=数字へ変換、1=アルファベットへ変換
    • [1] 変換する数字またはアルファベット(どちらも上限なし)

[→Excel列名変換問題で第2回社内プログラミングコンテストを開催してみた(前編)]

問題 2 に対する私の回答

問題 1 回答

#!/bin/perl

if ($ARGV[0] =~ /[A-Za-z]+/)
{
  print &convert(uc($ARGV[0])) . "\n";
}
else
{
  &usage();
}
exit 0;

sub usage
{
  print 'Usage: ',$0,' input',"\n";
}

sub convert
{
  my $input  = shift;
  my $output = 0;

  my $rank = 0;
  foreach my $char ( reverse(split(//,$input)) )
  {
    my $num = index('ABCDEFGHIJKLMNOPQRSTUVWXYZ', $char) + 1;
    $output += $num * (26 ** $rank++);
  }

  return $output;
}

アプローチとしては、3 位の方と同等ですね。一応、入力パラメータチェックしてます。

問題 2 回答

#!/bin/perl

if ($ARGV[0] eq '0' and $ARGV[1] =~ /[A-Za-z]+/)
{
  print &convert(uc($ARGV[1])) . "\n";
}
elsif ($ARGV[0] eq '1' and $ARGV[1] =~ /[0-9]+/ and $ARGV[1] > 0)
{
  print &rev_convert($ARGV[1]) . "\n";
}
else
{
  &usage();
}
exit 0;

sub usage
{
  print 'Usage: ',$0,' dir input',"\n";
  print '       dir 0 : alpha to num [input must be alphabet]'."\n";
  print '       dir 1 : num to alpha [input must be number greater than 0]'."\n";
}

sub convert
{
  my $input  = shift;
  my $output = 0;

  my $rank = 0;
  foreach my $char ( reverse(split(//,$input)) )
  {
    my $num = index('ABCDEFGHIJKLMNOPQRSTUVWXYZ', $char) + 1;
    $output += $num * (26 ** $rank++);
  }

  return $output;
}

sub rev_convert
{
  my $input  = shift;
  my $output = '';
  my @chars = split(//,'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
  my $base = @chars;

  my @stack = ();
  while ($input > $base)
  {
    my $num = $input % $base;
    push(@stack, $chars[$num - 1]);
    $input = int($input / $base);
    # Needs to decrement $input if $input value is in multiple of $base
    if ($num == 0)
    {
      $input--;
    }
  }
  # Now handles within $base case
  push(@stack, $chars[$input % $base - 1]);

  return join('',reverse(@stack));
}

入力数値が基数にあたる 26 の倍数の時、桁を正しく処理する必要があるのがトリッキーな部分になるでしょうか。

一応、ざっくり確認したつもりですが、間違っていたらごめんなさい。

[2010.11.04 03:30 追記] perl の場合、配列に対して負のインデックスを指定すると、配列の後ろからアクセスするという仕様があります。なので、$input % $base の結果が 0 になる場合、@stack には "Z" が格納されます。

このエントリーをはてなブックマークに追加

スポンサーリンク

<< 十歳になった娘へ :: ハーフマラソンに挑戦してみた >>