LoginSignup
3
2

More than 5 years have passed since last update.

シェル芸 Project Euler - Problem 17

Last updated at Posted at 2016-01-22

はじめに

@gin_135 さんの「Project Eulerをシェル芸で解いてみる」シリーズが大変面白いです.
大抵は @gin_135 さんの回答, @eban さんの別解を楽しく読ませていただき, 読んだだけで分からないものは, 手元で再現などして楽しんでいますが, 方針として大きく異なる別解を思いついたものについて, コメント欄に長文も失礼ですので, 自分のエントリを立てようと思います.

「Project Euler」の解説, 問題文の引用などは省略します.

本エントリは Problem 17 (問題原文, 問題文日本語訳) の解答です.

部分問題「英語化」の解答

数値を英語に, -例えば 342 を "three hundred and forty-two" に- 変換するシェルスクリプト (1 以上 1000 以下の自然数のみ対応) を独立して用意し, 全体解答はこれを用いて解きます.

number.sh
#!/bin/sh

o=(one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen)
t=(twenty thirty forty fifty sixty seventy eighty ninety)

if [ $1 -lt 20 ]
then
  eval echo '${o['`expr $1 - 1`']}'
elif [ $1 -lt 100 ]
then
  mod10=`expr $1 % 10`
  if [ $mod10 -eq 0 ]
  then
    eval echo '${t['`expr $1 / 10 - 2`']}'
  else
    eval echo '${t['`expr $1 / 10 - 2`']}'"-"`eval $0 $mod10`
  fi
elif [ $1 -lt 1000 ]
then
  mod100=`expr $1 % 100`
  if [ $mod100 -eq 0 ]
  then
    eval echo '${o['`expr $1 / 100 - 1`']} hundred'
  else
    eval echo '${o['`expr $1 / 100 - 1`']} hundred and '`eval $0 $mod100`
  fi
elif [ $1 -eq 1000 ]
then
  echo "one thousand"
fi

のようなシェルスクリプトを用意します.

場合分けで

  • 20 未満は配列引き
  • (20 以上) 100 未満は, 10 で割った商を配列引き, 10 で割った余りを自身の再起呼び出しで処理, 両者を結合
  • (100 以上) 1000 未満は, 100 で割った商を配列引き, 100 で割った余りを自身の再帰呼び出しで処理, 両者を結合
  • 1000 固定文字列

で解答を出力しています.

変数展開した文字列を含む文字列を eval で再度展開しています.
四則演算は expr にやらせています.
配列を使っているので, オリジナルの Bourne Shell では使えません.

$ /bin/sh --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin15)
Copyright (C) 2007 Free Software Foundation, Inc.
$ ./number.sh 342
three hundred and forty-two
$ ./number.sh 115
one hundred and fifteen

全体問題の解答

$ seq 1 1000 | xargs -n 1 ./number.sh | tr -d '\n -' | wc -m | tr -d ' '

のようにします.

  • seq で自然数の生成
  • xargs -n 1 で, それぞれの入力行に対して ./number.sh を実行
  • tr で改行, 空白, ハイフンの除去
  • wc -m で文字のカウント
  • 再度 tr で余分な空白の除去

です.

参考

@gin_135 さんの解答「Project Eulerをシェル芸で解いてみる(Problem 17)
@eban さんの解答「Project Euler Problem 17 #シェル芸 - jarp


配列使わない版

追記 2016/01/26

number.sh の bash の配列を使わない版も書いてみました.

numberb.sh

numberb.sh
#!/bin/sh

p=$0
n=$1
o="one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen"
t="twenty thirty forty fifty sixty seventy eighty ninety"

if [ $n -lt 20 ]
then
  set $o
  shift `expr $n - 1`
  echo $1
elif [ $n -lt 100 ]
then
  mod10=`expr $n % 10`
  set $t
  shift `expr $n / 10 - 2`
  if [ $mod10 -eq 0 ]; then echo $1; else
    eval echo '$1'"-"`eval $p $mod10`
  fi
elif [ $n -lt 1000 ]
then
  mod100=`expr $n % 100`
  set $o
  shift `expr $n / 100 - 1`
  if [ $mod100 -eq 0 ]; then echo "$1 hundred"; else
    eval echo '$1 hundred and '`eval $p $mod100`
  fi
elif [ $n -eq 1000 ]
then
  echo "one thousand"
fi

実行例

$ ./numberb.sh 342
three hundred and forty-two
$ ./numberb.sh 115
one hundred and fifteen

原理

set を使い, 文字列を空白で区切って, $1, $2, ... に代入し, 必要回数 shift して, $1 を参照することで, 擬似的に配列を実現します.

$ set a b c d e f
    # $1=a, $2=b, ... となっている
$ shift 3
    # 3回シフト, $1=d, $2=e, ... となっている.
$ echo $1
d
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2