2002/3〜2002/4

 Yささんの一言から始まったこの話題、 結構大きな波紋を呼んで、各スクリプト言語の比較に格好の材料となりました。

[お題]簡易足し算器

※ちなみに"簡易"なので入力が数値であるか等のチェックは省略
どなたかその他の(スクリプト)言語による例をお願いいたします(^o^)/

awk

作者: Yさ
スクリプト: add.awk
{ if($1!=0) total+=$1; else exit; }
END{ print "total = ", total; }

実行方法

gawk -f add.awk

Tcl(1)

作者: ビットウォークの高橋

Tclの場合の一例を紹介します。上記と同様、数値が入力されているかなど、 細かなチェックはしていません。
一般的なTclのスクリプト起動のやりかた(最初の3行)から、ちょっと余計な機能を足しました。

スクリプト: addition.tcl
#!/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" "$@"

# 初期値
set total 0

# コマンド引数の読み込み
if {$argc} {
    foreach value $argv {
        set total [expr $total + $value]
    }
}

# 0が入力されるまでループ
while {[set value [gets stdin]] != 0} {
    set total [expr $total + $value]
}

# 合計を出力
puts "合計:$total"

exit

実行方法

LinuxなどUNIX系シェル上で実行するには、次のようにします($はシェルのプロ ンプトです)。
$ chmod +x ./addition.tcl
$ ./addition.tcl
2
4
6
8
10
0
合計:30
(実行時に引数を付けてもOK)
$ ./addition.tcl 2 4 6
8
10
0
合計:30

Tcl(2)

作者: ビットウォークの高橋

あまり凝った作りにしてもなんですが、数値判定のルーチンを追加しました。
Tclではcatchコマンドを使って演算エラーを検出する方法が簡単ですが、練習用 にと正規表現を使ってみました。1.2345e+67のような指数形式には今回対応して いません。
なお、今回使った正規表現はチェックが不十分なので無駄や欠陥があるかもしれ ません。どうも無責任ですみません。

スクリプト: addition2.tcl
#!/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" "$@"

# add_number
# n = n + m を計算する
proc add_number {n m} {
    # 実数または整数であるかチェック
    if {[regexp {^[-+]?[0-9]+\.*[0-9]*$} $m]} {
        return [expr $n + $m]
    } else {
        puts "${m}は数字でないため、スキップしました。"
        return $n
    }
}

#-------
# メイン
# ------

# 初期値
set total 0

# コマンド引数の読み込み
if {$argc} {
    foreach value $argv {
        set total [add_number $total $value]
    }
}

# 0が入力されるまでループ
while {[set value [gets stdin]] != 0} {
    set total [add_number $total $value]
}

# 合計を出力
puts "合計:$total"

exit

実行方法

$ chmod +x ./addition2.tcl
$ ./addition2.tcl
-2.5
2e+5
2e+5は数字でないため、スキップしました。
4.5
+6.5
8
10.
0
合計:26.5

Tcl(3)

作者: ビットウォークの高橋

所詮TclはTclらしく、ということでcatchコマンドを使った例です。

スクリプト: addition3.tcl
#!/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" "$@"

# add_number
# n = n + m を計算する
proc add_number {n m} {
    if {[catch {set n [expr $n + $m]} errmsg]} {
        puts $errmsg
    }
    return $n
}

#-------
# メイン
# ------

# 初期値
set total 0

# コマンド引数の読み込み
if {$argc} {
    foreach value $argv {
        set total [add_number $total $value]
    }
}

# 0が入力されるまでループ
while {[set value [gets stdin]] != 0} {
    set total [add_number $total $value]
}

# 合計を出力
puts "合計:$total"

exit

実行方法

$ chmod +x ./addition3.tcl
$ ./addition3.tcl
-2.5
2e-2
2e
syntax error in expression "-2.48 + 2e"
4.5
+6.5
8
10.
0
合計:26.52

Tcl(4)

作者: ビットウォークの高橋

Tcl/Tk8.1以上では次のように記述することも出来ます。自分自身は通常のコー ディングにおいて、ここまで日本語名を多用しませんが、やろうと思えばここま でできる、という参考にと紹介しました。

スクリプト: addition4.tcl
#!/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" "$@"

# 加算処理
# 「数1 = 数1 + 数2」を計算する。
# Tclでは全てコマンドとして処理されるので
# +=のような代入演算子がありません。
proc 加算処理 {数1 数2} {
    if {[catch {set 数1 [expr ${数1} + ${数2}]} エラー]} {
        puts ${エラー}
    }
    return ${数1}
}

#-------
# メイン
# ------

# 初期値
set 合計 0

# コマンド引数の読み込み
if {$argc} {
    foreach 引数 $argv {
        set 合計 [加算処理 ${合計} ${引数}]
    }
}

# 0が入力されるまでループ
while {[set 入力値 [gets stdin]] != 0} {
    set 合計 [加算処理 ${合計} ${入力値}]
}

# 合計を出力
puts "合計:${合計}"

exit

Perl(1)

作者: 中村 のりつぐ

はじめ last でなくて break って書いてしまった (^_^;

スクリプト: add.pl
#!/usr/bin/env perl

while (<>) {
    if ($_ != 0) {
        $total += $_;
    } else {
        last;
    }
}
print "total = $total\n";

実行方法

perl add.pl

Perl(2)

作者: 中村 のりつぐ

引数対応版

スクリプト: add.pl
#!/usr/bin/env perl

while (@ARGV) {
    $total += shift(@ARGV);
}

while (<>) {
    if ($_ != 0) {
        $total += $_;
    } else {
        last;
    }
}
print "total = $total\n";

Perl(3)

作者: 中村 のりつぐ

できるだけ短く

スクリプト: add.pl
#!/usr/bin/env perl

while (($v = <>) != 0) {
    $total += $v;
}
print "total = $total\n";

Python(1)

作者: Fe2+
スクリプト: add.py
#!/usr/local/bin/python
from sys import argv
from operator import add

sum = reduce(add, map(int, argv[1:]), 0)
while 1:
      buf = raw_input()
      if buf == '0': break
      sum += int(buf)
print sum

Python(2)

作者: 機械伯爵

 そも、input()という標準関数があるので、stdinなんて インポートする必要ないです。
 input関数は文字でなく「Python文」を受け取りますので、 値さえあればOKなので、実は「リテラル」でもよいわけでして、 これで、「文字列→数値変換」も全く必要ありません。
※文字を受け取りたい場合はraw_input()を利用。
 ただし、ファイル入力で、入力ファイル名をオプションから 受け取ろうとすると、こうなります。

スクリプト: add2.py
import sys

f = open(sys.argv[1],"r")
i = 1
s = 0
while i:
  i = int(f.readline())
  s += i
print "Total %i yen" % s

Python(3)

作者: 機械伯爵

 Pythonの場合、基本はコレで十分。

スクリプト:
i = 1
s = 0
while i:
  i = input(">>> ")
  s += i
print "Total %i yen" % s

実行方法

 Windowsでダブルクリック起動の場合は、
raw_input()
の1行を最後にいれておくと、Enterキーを押すまでウィンドウが 消えません。
 UNIXの場合は、
#! /usr/bin/python
で、実行形式にしてもいいけど、Pythonに直接渡した方が簡単かも・・・

sh

作者: わたなべひろふみ

bashやzshやkshならexprを使わずに

  sum=$(($sum + $line))

と書けます。bashかzshなら

  sum=$[$sum + $line]

とも書くこともできます。 スクリプト:

#!/bin/sh

sum=0
while read line
do
  if [ $line = 0 ]; then break; fi
  sum=`expr $sum + $line`
done
echo "total = $sum"

Scheme(1)

作者: 廣島 勉

MIT で アリゴリズムの講義に使われる Scheme の例です. (初心者の言語とはいいがたいけど参考までに)

;;; 解1
;;; 名前付き let による終端再帰を使った素直な解
(display
 (let loop ((total 0))
   (let ((num (read)))
     (if (zero? num)
	 total
	 (loop (+ total num))))))

Scheme(2)

作者: 廣島 勉
;;; 解2
;;; 継続の呼び出しでループから脱出するマニアな解
(display
 (let ((total 0))
   (call-with-current-continuation
    (lambda (break)
      (let loop ()
	(let ((num (read)))
	  (if (zero? num)
	      (break total)
	      (begin
		(set! total (+ total num))		
		(loop)))))))))

Scheme(3)

作者: 廣島 勉
;;; 解3
;;; 継続の呼び出しでループを実現するひねくれた解
(display
 (let ((total 0))
   (let ((next (call-with-current-continuation (lambda (k) k))))
     (let ((num (read)))
       (if (zero? num)
	   total
	   (begin
	     (set! total (+ total num))
	     (next next)))))))

Scheme(4)

作者: 廣島 勉
;;; 解4
;;; 解2と解3を組み合せてループとループからの脱出を
;;; ともに継続で実現した病的な解
(display
 (let ((total 0))
   (call-with-current-continuation
    (lambda (break)
      (let ((next (call-with-current-continuation (lambda (k) k))))
	(let ((num (read)))
	  (if (zero? num)
	      (break total)
	      (begin 
		(set! total (+ total num))
		(next next)))))))))

Scheme(5)

作者: 廣島 勉
;;; 解5
;;; do を使った Scheme らしからぬ解
(display
 (do ((num (read) (read))
      (total 0 (+ total num)))
     ((zero? num) total)))

Emacs Lisp(1)

作者: 閑舎
(let ((loop t) (total 0))
  (while loop
	(setq num (read-minibuffer ""))
	(if (= num 0)
		(progn (setq loop nil)
			   (message (format "%f" total))
			   (sleep-for 2))
	  (setq total (+ total num))
)))

実行方法

最後のかっこの直後で Ctrl-x Ctrl-e

Emacs Lisp(2)

作者: 廣島 勉

Emacs Lisp でも Tcl と同じく Catch & Throw が使えますから, 以下も,解です.

(message (format "%f"
		 (catch 'break
		   (let ((total 0))
		     (while t
		       (let ((num (read-minibuffer "")))
			 (if (= num 0)
			     (throw 'break total)
			   (setq total (+ total num)))))))))

Common Lisp(1)

作者: 廣島 勉

Common Lisp も Scheme とは異なり, 正しい終端再帰を実装していませんでした.
で,Common Lisp の場合です.

;;; 解1
;;; do を使った場合
(print
 (do ((num (read) (read))
      (total 0 (+ total num)))
     ((zerop num) total)))

Common Lisp(2)

作者: 廣島 勉
;;; 解2
;;; loop を使った場合
(print
 (let ((total 0))
   (loop
     (let ((num (read)))
       (when (zerop num) (return total))
       (setq total (+ total num))))))

Ruby

作者: sugi
スクリプト: add.rb
#! /usr/local/bin/ruby

sum=0
while 1
  print "数値を入力 > "
  break if (num=gets.to_f)==0
  sum+=num
end
puts "数値の合計は #{sum} です"

実行方法

ruby add.rb

Csh

作者: sugi

ついでにみようみまねで覚えた csh の場合、

スクリプト:
#! /bin/csh

set num=1
set sum=0
while ($num != 0)
  echo -n "数値を入力 > "
  set num = $<
  @ sum = $sum + $num
end
echo "数値の合計は $sum です"

TeX

作者: 廣島 勉

TeX Book で Knuth 先生は,
「特殊な効果が欲しいときや, コンピュータの使用料金をあまり気にしなくていいときには, 実際に TeX をプリミティブなプログラミング言語として活用してみるのもいい だろう.」
と述べておられます.(ASCII 版訳本より抜粋)
で,こんなになります.

スクリプト: add.tex
\newcount\total=0
\newif\ifcont
\loop
 \message{ Please Input Integer>> }
 \read-1 to\answer
 \ifnum0=\answer
  \contfalse
 \else
  \advance\total by \answer
  \relax
  \conttrue
 \fi
 \ifcont
\repeat
\message{ Total is \the\total}
\end

実行方法

これを TeX にかけると 端末から整数の入力を受けつけ, 端末に結果を出力した後, 空ページの dvi ファイルと 入力記録も含めた log ファイルを作ります.

Sed

作者: Zazel

TeXが出てきたからには、sed版も公開せねばなるまい。 # これが病的と呼ばれる一因か?! (^_^)
ちなみに負の数は実装していませんが、小数や多倍長演算はできます。

スクリプト: addition.sed
s/^ +//
s/ +$//
/^[0-9.][0-9.]*$/!q
/^0$/{
    x
    s/^/total = /
    q
}

x
/^$/d
    
G
s/\n/ /
s/$/=/

t nop
:nop

:split1
s/^\([0-9]*\.\)\([0-9]\)\([0-9]*\) \([0-9]*\.\)\([0-9]\)\([0-9]*\)=\(.*\)/\1\3 \4\6=\7 \2\5/
t split1
:split2
s/\.\([0-9]\)\(.*=.*\)/.\2 \1/
t split2
s/\.//g
s/=/=./
:split3
s/^\([0-9]*\)\([0-9]\) \([0-9]*\)\([0-9]\)=/\1 \3=\2\4 /
t split3
:split4
s/\([0-9]\)\([0-9]\)\(.*=\)/\1 \2\3/
t split4
s/=/ /
s/   */ /g
s/ \././
s/\. /./

:loop
s/^[^ ]/ &/

s/00*\([0-9]\)/\1/g
s/\([0-9]\)0/\1/g

s/11/2/g
s/12/3/g
s/13/4/g
s/14/5/g
s/15/6/g
s/16/7/g
s/17/8/g
s/18/9/g
s/\([ .][0-9]*\)19/1\10/g

s/21/3/g
s/22/4/g
s/23/5/g
s/24/6/g
s/25/7/g
s/26/8/g
s/27/9/g
s/\([ .][0-9]*\)28/1\10/g
s/\([ .][0-9]*\)29/1\11/g

s/31/4/g
s/32/5/g
s/33/6/g
s/34/7/g
s/35/8/g
s/36/9/g
s/\([ .][0-9]*\)37/1\10/g
s/\([ .][0-9]*\)38/1\11/g
s/\([ .][0-9]*\)39/1\12/g

s/41/5/g
s/42/6/g
s/43/7/g
s/44/8/g
s/45/9/g
s/\([ .][0-9]*\)46/1\10/g
s/\([ .][0-9]*\)47/1\11/g
s/\([ .][0-9]*\)48/1\12/g
s/\([ .][0-9]*\)49/1\13/g

s/51/6/g
s/52/7/g
s/53/8/g
s/54/9/g
s/\([ .][0-9]*\)55/1\10/g
s/\([ .][0-9]*\)56/1\11/g
s/\([ .][0-9]*\)57/1\12/g
s/\([ .][0-9]*\)58/1\13/g
s/\([ .][0-9]*\)59/1\14/g

s/61/7/g
s/62/8/g
s/63/9/g
s/\([ .][0-9]*\)64/1\10/g
s/\([ .][0-9]*\)65/1\11/g
s/\([ .][0-9]*\)66/1\12/g
s/\([ .][0-9]*\)67/1\13/g
s/\([ .][0-9]*\)68/1\14/g
s/\([ .][0-9]*\)69/1\15/g

s/71/8/g
s/72/9/g
s/\([ .][0-9]*\)73/1\10/g
s/\([ .][0-9]*\)74/1\11/g
s/\([ .][0-9]*\)75/1\12/g
s/\([ .][0-9]*\)76/1\13/g
s/\([ .][0-9]*\)77/1\14/g
s/\([ .][0-9]*\)78/1\15/g
s/\([ .][0-9]*\)79/1\16/g

s/81/9/g
s/\([ .][0-9]*\)82/1\10/g
s/\([ .][0-9]*\)83/1\11/g
s/\([ .][0-9]*\)84/1\12/g
s/\([ .][0-9]*\)85/1\13/g
s/\([ .][0-9]*\)86/1\14/g
s/\([ .][0-9]*\)87/1\15/g
s/\([ .][0-9]*\)88/1\16/g
s/\([ .][0-9]*\)89/1\17/g

s/\([ .][0-9]*\)91/1\10/g
s/\([ .][0-9]*\)92/1\11/g
s/\([ .][0-9]*\)93/1\12/g
s/\([ .][0-9]*\)94/1\13/g
s/\([ .][0-9]*\)95/1\14/g
s/\([ .][0-9]*\)96/1\15/g
s/\([ .][0-9]*\)97/1\16/g
s/\([ .][0-9]*\)98/1\17/g
s/\([ .][0-9]*\)99/1\18/g

/[0-9][0-9]/b loop

s/ //g
x
d

実行方法

sed -f addition.sed

Metafont

作者: 廣島 勉

Metafont の場合です.
log ファイルができるのでご注意を...

スクリプト: add.mf
total := 0;
forever:
 message ">> ";
 num := scantokens readstring;
 exitif num = 0;
 total := total + num;
endfor;
show total;
end

実行方法

mf add.mf

PostScript

作者: 廣島 勉

さすがに文法が独特で,やっとできました.
さらに,Adobe のページにリファレンスマニュアルがあったので, 索引を調べたら,いいオペレータを見つけました.

スクリプト: add.ps
/buf 16 string def
/total 0 def
/fd (%stdin) (r) file def
{ %loop
  /num fd buf readline pop cvr def
  num 0 eq { exit } if
  /total total num add def
} loop
total ==
quit
実行方法1
Linux の GNU Ghostscript 5.50 でのみ動作確認しました.
% gs -sDEVICE=nullpage -q add.ps
実行方法2
Windows XP の GNU Ghostscript 7.04 での動作確認(閑舎)。
gswin32c -sDEVICE=nullpage -q add.ps

cmd(WindowsNT/2000/XP)

作者: わたなべひろふみ

cmd.exeだと足し算もできます。

スクリプト: add.cmd
@echo off
set sum=0
:loop
  set /p line=
  if (%line%) == (0) goto break
  set /a sum=%sum%+%line%
goto loop
:break
echo total = %sum%

Icon(1)

作者: 風つかい

素直に書くと次のようになります。

スクリプト: add01.icon
####################
# 簡易足し算器 Icon版1
####################
# add01.icn Rev.1.0 2002/05/06 windy 風つかい H.S.
# Usage   : add01   # 数字+改行入力、0+改行を入力すると総和を出力
# This file is in the public domain.
procedure main()
  sum := 0                        # 総和初期化
  while line := read() do {       # (標準入力から)入力を読み込んで
    if line == "0" then break     # "0"ならばループを抜ける
    else sum +:= numeric(line)    # そうでなければ、数値変換し足し込む
  }
  write("Total = ",sum)           # 総和を出力
end

Icon(2)

作者: 風つかい

もっと短くしようとすると、こんな風かな。

スクリプト: add02.icon
####################
# 簡易足し算器 Icon版2
####################
# add02.icn Rev.1.0 2002/05/06 windy 風つかい H.S.
# Usage   : add02   # 数字+改行入力、0+改行を入力すると総和を出力
# This file is in the public domain.
procedure main()
  sum := 0                # 総和を初期化
  while sum +:= numeric(("0" ~== read()))
  #     ↑数値変換して足す ↑読み込んだ値が 0でなければ
  write("Total = ",sum)   # 総和を出力
end