Yささんの一言から始まったこの話題、 結構大きな波紋を呼んで、各スクリプト言語の比較に格好の材料となりました。
- awk
- Tcl(1)
- Perl(1)
- Python(1)
- sh
- Scheme(1)
- Emacs Lisp(1)
- Common Lisp(1)
- Ruby
- Csh
- TeX
- Sed
- Metafont
- PostScript
- cmd
- Icon(1)
[お題]簡易足し算器
- [内容]入力された数字を合計し、表示する。
- 0が入力されるまで数字を受け付け、合計し続ける。
- 0が入力されるとそれまでの合計を表示する。
※ちなみに"簡易"なので入力が数値であるか等のチェックは省略
どなたかその他の(スクリプト)言語による例をお願いいたします(^o^)/
awk
{ if($1!=0) total+=$1; else exit; } END{ print "total = ", total; }
実行方法
gawk -f add.awk
Tcl(1)
Tclの場合の一例を紹介します。上記と同様、数値が入力されているかなど、
細かなチェックはしていません。
一般的なTclのスクリプト起動のやりかた(最初の3行)から、ちょっと余計な機能を足しました。
#!/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のような指数形式には今回対応して
いません。
なお、今回使った正規表現はチェックが不十分なので無駄や欠陥があるかもしれ
ません。どうも無責任ですみません。
#!/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)
#!/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()を利用。
ただし、ファイル入力で、入力ファイル名をオプションから
受け取ろうとすると、こうなります。
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-eEmacs 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
#! /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
ついでにみようみまねで覚えた 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 版訳本より抜粋)
で,こんなになります.
\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
TeXが出てきたからには、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 ファイルができるのでご注意を...
total := 0; forever: message ">> "; num := scantokens readstring; exitif num = 0; total := total + num; endfor; show total; end
実行方法
mf add.mf
PostScript
さすがに文法が独特で,やっとできました.
さらに,Adobe のページにリファレンスマニュアルがあったので,
索引を調べたら,いいオペレータを見つけました.
/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