連続する数列をハイフンでまとめるシェルスクリプト

おもしろい!こうゆう問題が大好き。実用的であり、かつ頭の体操になる。

問題

並んだ数字を確認用にわかりやすくする為に、ソートされた数字の列をハイフンで繋ぐにはどうすればよいでしょうか?
数列は見づらいものです。ただ数字を並べているだけでは、ソートしてもどの数字が抜けているかを判断するために、順に目で追ってチェックする必要があります。
rubyではそういった配列をわかりやすく表現する為に「1...3」と表現する方法が用意されているのですが、利用者は、「1 2 3 5」というような数字の羅列を入れてくるかもしれません。さて、rubyでこれらをハイフンで繋ぐためにはどのように処理する必要があるでしょうか。


仕様

  • 数値は、半角スペースで区切られた文字列で渡されます。
  • 続いている部分は、最初の数値と最後の数値を-(ハイフン)で繋いだ表記にします。
  • 連続が1回の場合(前の数も後ろの数も連続でない)は、-(ハイフン)では繋ぎません。
  • 出力は、「,」(カンマ)と半角スペースで区切られた文字列でなければなりません。



"1 2 3" => "1-3."
"1 2 3 5 7 8" => "1-3, 5, 7-8."
"1 3 4 5 7" => "1, 3-5, 7."
Rubyでどう書く?:連続した数列を範囲形式にまとめたい - builder by ZDNet Japan


すでに、RubyPerlのサンプルが出揃っている。自分は何を使おうか?と考え、最も基本的なシェルスクリプト(sh)でやってみようと思い立った。shならほとんどすべての環境で動くことを期待して。自分が手作業行う時の頭の中の手順を、素直にコードにしてみた。

  • hyphen_num.sh
#!/bin/sh
res=''
mode=','
last=$1
shift

while [ -n "$1" ]
do
  if [ `expr $1 - $last` -eq 1 ]; then
    [ "$mode" = "-" ] || res="${res}${last}-"
    mode='-'
  else
    res="${res}${last},"
    mode=','
  fi
  last=$1
  shift
done

res="${res}${last}"
echo $res
  • テストしてみた。
$ hyphen_num.sh 1 2 3
1-3

$ hyphen_num.sh 1 2 3 5 7 8
1-3,5,7-8

$ hyphen_num.sh 1 3 4 5 7
1,3-5,7
  • 問題のサンプルとは若干違っているが、できた!
    • カンマの後ろにスペースはない。
    • 数列の終わりにピリオドはない。



なるほどー。

#!/bin/bash
# or
#!/bin/ksh

read i
s=${i%% *}
let p=x=s-1


for n in $i x; do
  ((n-p-1)) && {
    ((p-x)) && o=$o-$p
    o=$o,\ $n
    x=$n
  }
  p=$n
done

o=$s${o#-$s}
echo ${o%, x}.
連続する数列をハイフンでまとめるピュアシェルスクリプト - ダメ出し Blog
% echo 0 1 2 3 5 7 8 23 24 25 50 100 101 102 103 110 | \
  awk '{a=$1;for(i=0;b=_$++i;a=b)if(a+1==b)$i=-$i;OFS=",";gsub(/,-([0-9]+,-)*/,"-")}1'
0-3,5,7-8,23-25,50,100-103,110
Just another Ruby porter, 2013-11-c