いろんな言語でハノイの塔
差分
このページの2つのバージョン間の差分を表示します。
両方とも前のリビジョン前のリビジョン次のリビジョン | 前のリビジョン | ||
いろんな言語でハノイの塔 [2019/06/27 22:40] – [性能] araki | いろんな言語でハノイの塔 [2021/04/30 12:38] (現在) – [Rust] araki | ||
---|---|---|---|
行 29: | 行 29: | ||
^Ruby |1.8.7 |Celeron N3150 (1.60GHz) |Vine Linux 6.5 |32 | | ^Ruby |1.8.7 |Celeron N3150 (1.60GHz) |Vine Linux 6.5 |32 | | ||
^Python |2.6.6 |Celeron N3150 (1.60GHz) |Vine Linux 6.5 |52 | | ^Python |2.6.6 |Celeron N3150 (1.60GHz) |Vine Linux 6.5 |52 | | ||
+ | ^bash |4.3.42 |Celeron N3150 (1.60GHz) |Vine Linux 6.5 |122 | | ||
+ | ^awk (gawk) |3.1.8 |Celeron N3150 (1.60GHz) |Vine Linux 6.5 |8 | | ||
+ | ^go|1.16.0|Core i3 5010U (2.1GHz) |Ubuntu 20.4 LTS|0.004 | | ||
+ | ^Rust|1.47.0|Core i3 5010U (2.1GHz) |Ubuntu 20.4 LTS|0.004 | | ||
===== プログラム ===== | ===== プログラム ===== | ||
==== PowerShell ==== | ==== PowerShell ==== | ||
行 44: | 行 48: | ||
なお、デジタル署名するのが面倒くさかったので、実行にあたっては、実行ポリシーを'' | なお、デジタル署名するのが面倒くさかったので、実行にあたっては、実行ポリシーを'' | ||
((管理者権限でPowerShellを起動して'' | ((管理者権限でPowerShellを起動して'' | ||
+ | 管理者権限を使ってシステム全体のポリシーを変更するのがコワイ場合は、 | ||
+ | < | ||
+ | PS C: | ||
+ | </ | ||
+ | のようにして、実行中のプロセス内でのみ許可することも出来る。 | ||
+ | この方法には管理者権限は不要なので、気軽にスクリプトを試すことが出来る。 | ||
+ | |||
一般的な関数型言語が採用している、関数呼び出しと違い、引数を括弧でくくってカンマで区切ってはいけない。 | 一般的な関数型言語が採用している、関数呼び出しと違い、引数を括弧でくくってカンマで区切ってはいけない。 | ||
括弧でくくってカンマで区切るとそれは配列として、関数の第一引数に全て渡されてしまう。 | 括弧でくくってカンマで区切るとそれは配列として、関数の第一引数に全て渡されてしまう。 | ||
行 879: | 行 890: | ||
</ | </ | ||
+ | ==== Python ==== | ||
+ | === 能書き === | ||
+ | Perl、Rubyと来たら、Pythonを出さないわけにはいかないでしょう。 | ||
+ | 今日では恐らくPythonが最も広く使われているのではないかと思います。 | ||
+ | Pythonもオブジェクト指向言語であり、Perlよりずっとすっきりと記述できます。 | ||
+ | ただ、個人的には、インデントでブロックを構成する仕様が好きになれなくて、PythonでもRubyでもいいならRubyを使いたい派です。 | ||
+ | インデントによるブロックが嫌いなのは、エディタによっては、勝手にいくつかのスペースをタブに置きかえる場合があり、これが発動すると、見た目にはOKなブロックが実は破綻しているなど、コーディングに集中できないケースがあるからです。 | ||
+ | こちらも、Perlに比べて、実行速度の面では劣る傾向があります。 | ||
+ | === コード === | ||
+ | こちらも、特にひねりもない実装かと思います。 | ||
+ | '' | ||
+ | つまり、クラスライブラリとそれに付随するサンプル、あるいはテストコードをひとまとめにしておくことも出来るのはなかなかに便利だと思います。 | ||
+ | < | ||
+ | # | ||
+ | import optparse, sys | ||
+ | |||
+ | class Hanoi: | ||
+ | """ | ||
+ | def __init__(self, | ||
+ | self.size = size | ||
+ | |||
+ | def size(self): | ||
+ | return self.size | ||
+ | |||
+ | def do(self, n = None, a = 1, b = 2, c = 3): | ||
+ | if n is None: | ||
+ | n = self.size | ||
+ | if a == b or b == c or c == a: | ||
+ | print "Same placement: ", a, ", ", b, ", and ", c | ||
+ | raise | ||
+ | if n == 0: | ||
+ | return | ||
+ | self.do(n - 1, a, c, b) | ||
+ | print " | ||
+ | self.do(n - 1, c, b, a) | ||
+ | |||
+ | if __name__ == ' | ||
+ | parser = optparse.OptionParser() | ||
+ | parser.add_option(' | ||
+ | args,r = parser.parse_args() | ||
+ | |||
+ | hanoi = Hanoi(args.size) | ||
+ | hanoi.do() | ||
+ | </ | ||
+ | === 実行結果 === | ||
+ | < | ||
+ | araki@chive[146]% time ./hanoi.py -n 4 | ||
+ | Disc 1 moved from 1 to 3 | ||
+ | Disc 2 moved from 1 to 2 | ||
+ | Disc 1 moved from 3 to 2 | ||
+ | Disc 3 moved from 1 to 3 | ||
+ | Disc 1 moved from 2 to 1 | ||
+ | Disc 2 moved from 2 to 3 | ||
+ | Disc 1 moved from 1 to 3 | ||
+ | Disc 4 moved from 1 to 2 | ||
+ | Disc 1 moved from 3 to 2 | ||
+ | Disc 2 moved from 3 to 1 | ||
+ | Disc 1 moved from 2 to 1 | ||
+ | Disc 3 moved from 3 to 2 | ||
+ | Disc 1 moved from 1 to 3 | ||
+ | Disc 2 moved from 1 to 2 | ||
+ | Disc 1 moved from 3 to 2 | ||
+ | 0.043u 0.009s 0:00.06 66.6% 0+0k 112+0io 0pf+0w | ||
+ | araki@chive[147]% | ||
+ | </ | ||
+ | ==== bash ==== | ||
+ | === 能書き === | ||
+ | bashは、Bone Shell (sh)に変わって、現在のUNIX系システムの標準シェルの地位を確保しているシェルです。 | ||
+ | シェルスクリプトはそれなりに強力であり、簡単な処理であれば、これだけでこなすことができます。 | ||
+ | シェルがないUNIX環境は事実上ないので、どんな環境でも実行できるポータビリティは魅力です。 | ||
+ | |||
+ | もちろん、本格的なプログラム言語に比べて見劣りする部分も少なくないですが、DOSのバッチに比べればはるかに強力です。 | ||
+ | PowerShellとであれば、いい勝負でしょうか。 | ||
+ | PowerShellはパイプでオブジェクトも渡せるので、システムの機能をフルに引き出すという点ではbashに勝りますが、他は似たようなものでしょう。 | ||
+ | === コード === | ||
+ | 特に凝ったことはしていません。基本的には shでも動くんじゃないかと思います。 | ||
+ | 関数の引数は関数ローカルになっているので、ローカル変数を使わない限り普通に再帰もおこなえます。 | ||
+ | このため再帰呼び出しの際に '' | ||
+ | |||
+ | < | ||
+ | # | ||
+ | |||
+ | function hanoi() | ||
+ | { | ||
+ | if [ ${1} -gt 0 ]; then | ||
+ | hanoi `expr ${1} - 1` ${2} ${4} ${3} | ||
+ | echo "Disc ${1} moved from ${2} to ${3}" | ||
+ | hanoi `expr ${1} - 1` ${4} ${3} ${2} | ||
+ | fi | ||
+ | } | ||
+ | |||
+ | size=3 | ||
+ | while [ $# -gt 0 ]; do | ||
+ | case $1 in | ||
+ | " | ||
+ | echo " -n < | ||
+ | echo " -h: show this message." | ||
+ | exit 1 | ||
+ | ;; | ||
+ | " | ||
+ | size=$2 | ||
+ | shift; shift; | ||
+ | ;; | ||
+ | *) | ||
+ | shift | ||
+ | esac | ||
+ | done | ||
+ | hanoi ${size} 1 2 3 | ||
+ | </ | ||
+ | === 実行結果 === | ||
+ | < | ||
+ | araki@chive[152]% time ./ | ||
+ | Disc 1 moved from 1 to 3 | ||
+ | Disc 2 moved from 1 to 2 | ||
+ | Disc 1 moved from 3 to 2 | ||
+ | Disc 3 moved from 1 to 3 | ||
+ | Disc 1 moved from 2 to 1 | ||
+ | Disc 2 moved from 2 to 3 | ||
+ | Disc 1 moved from 1 to 3 | ||
+ | Disc 4 moved from 1 to 2 | ||
+ | Disc 1 moved from 3 to 2 | ||
+ | Disc 2 moved from 3 to 1 | ||
+ | Disc 1 moved from 2 to 1 | ||
+ | Disc 3 moved from 3 to 2 | ||
+ | Disc 1 moved from 1 to 3 | ||
+ | Disc 2 moved from 1 to 2 | ||
+ | Disc 1 moved from 3 to 2 | ||
+ | 0.051u 0.071s 0:00.13 92.3% 0+0k 0+0io 0pf+0w | ||
+ | araki@chive[153]% | ||
+ | </ | ||
+ | ==== awk ==== | ||
+ | === 能書き === | ||
+ | awkは、AT& | ||
+ | 一行野郎などと言われるように、本来は、単機能を実装したコマンドを、パイプでつないで、複雑な処理を実現するのが主な使い方であり、コードそのものをコマンドラインにタイプしてしまい、わざわざスクリプトファイルにしたりしないことも多い。 | ||
+ | < | ||
+ | $ cat resul.txt | awk ' | ||
+ | </ | ||
+ | などのように、することで、ファイルの特定カラムの値を' | ||
+ | |||
+ | 言語仕様が単純なこともあって、処理はそれなりに早い。 | ||
+ | |||
+ | また、パターンマッチングに正規表現が使えることも利点の一つである。 | ||
+ | === コード === | ||
+ | 普通の関数型言語の側面もあるので、そのまま、ストレートに実装してある。 | ||
+ | < | ||
+ | # | ||
+ | function hanoi(n, a, b, c) | ||
+ | { | ||
+ | if (n > 0) { | ||
+ | hanoi(n-1, a, c, b); | ||
+ | printf(" | ||
+ | hanoi(n-1, c, b, a); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | BEGIN { | ||
+ | size = 3; | ||
+ | for (i = 0 ; i < ARGC ; i++) | ||
+ | { | ||
+ | if (ARGV[i] == " | ||
+ | { | ||
+ | printf(" | ||
+ | exit(1); | ||
+ | } | ||
+ | if (ARGV[i] == " | ||
+ | { | ||
+ | size = ARGV[++i]; | ||
+ | } | ||
+ | } | ||
+ | hanoi(size, 1, 2, 3); | ||
+ | } | ||
+ | </ | ||
+ | === 実行結果 === | ||
+ | < | ||
+ | araki@chive[157]% time ./hanoi.awk -n 4 | ||
+ | Disc 1 moved from 1 to 3 | ||
+ | Disc 2 moved from 1 to 2 | ||
+ | Disc 1 moved from 3 to 2 | ||
+ | Disc 3 moved from 1 to 3 | ||
+ | Disc 1 moved from 2 to 1 | ||
+ | Disc 2 moved from 2 to 3 | ||
+ | Disc 1 moved from 1 to 3 | ||
+ | Disc 4 moved from 1 to 2 | ||
+ | Disc 1 moved from 3 to 2 | ||
+ | Disc 2 moved from 3 to 1 | ||
+ | Disc 1 moved from 2 to 1 | ||
+ | Disc 3 moved from 3 to 2 | ||
+ | Disc 1 moved from 1 to 3 | ||
+ | Disc 2 moved from 1 to 2 | ||
+ | Disc 1 moved from 3 to 2 | ||
+ | 0.003u 0.005s 0:00.00 0.0% 0+0k 0+0io 0pf+0w | ||
+ | araki@chive[158]% | ||
+ | </ | ||
+ | ==== Go ==== | ||
+ | === 能書き === | ||
+ | Goは比較的新しいコンパイラ言語である。 | ||
+ | 静的型付けを基本とする言語であるが、メモリ管理は言語側が行い、ガベージコレクションなどの機能も内包している。 | ||
+ | |||
+ | 関数型言語であり、構造体を使った、オブジェクト指向っぽい記述もできる。 | ||
+ | |||
+ | === コード === | ||
+ | |||
+ | コマンドラインの引数をとるために flagを、表示の加工用に fmtモジュールを利用している以外は、コード内で完結している。 | ||
+ | |||
+ | < | ||
+ | package main | ||
+ | |||
+ | import ( | ||
+ | " | ||
+ | " | ||
+ | ) | ||
+ | |||
+ | type Discs int | ||
+ | |||
+ | type Hanoi struct { | ||
+ | N Discs | ||
+ | a int | ||
+ | b int | ||
+ | c int | ||
+ | } | ||
+ | |||
+ | func NewTower(n int) *Hanoi { | ||
+ | h := new(Hanoi) | ||
+ | h.N = Discs(n) | ||
+ | h.a = 1 | ||
+ | h.b = 2 | ||
+ | h.c = 3 | ||
+ | return h | ||
+ | } | ||
+ | |||
+ | func (n Discs) doit(a int, b int, c int) { | ||
+ | if n > 0 { | ||
+ | (n - 1).doit(a, c, b) | ||
+ | fmt.Printf(" | ||
+ | (n - 1).doit(c, b, a) | ||
+ | } | ||
+ | } | ||
+ | |||
+ | func (h Hanoi) run() { | ||
+ | h.N.doit(h.a, | ||
+ | } | ||
+ | |||
+ | func main() { | ||
+ | n := flag.Int(" | ||
+ | flag.Parse() | ||
+ | hanoi := NewTower(*n) | ||
+ | hanoi.run() | ||
+ | } | ||
+ | </ | ||
+ | === 実行結果 === | ||
+ | |||
+ | < | ||
+ | araki@cherry: | ||
+ | Disc 1 moved from 1 to 3. | ||
+ | Disc 2 moved from 1 to 2. | ||
+ | Disc 1 moved from 3 to 2. | ||
+ | Disc 3 moved from 1 to 3. | ||
+ | Disc 1 moved from 2 to 1. | ||
+ | Disc 2 moved from 2 to 3. | ||
+ | Disc 1 moved from 1 to 3. | ||
+ | Disc 4 moved from 1 to 2. | ||
+ | Disc 1 moved from 3 to 2. | ||
+ | Disc 2 moved from 3 to 1. | ||
+ | Disc 1 moved from 2 to 1. | ||
+ | Disc 3 moved from 3 to 2. | ||
+ | Disc 1 moved from 1 to 3. | ||
+ | Disc 2 moved from 1 to 2. | ||
+ | Disc 1 moved from 3 to 2. | ||
+ | |||
+ | real 0m0.004s | ||
+ | user 0m0.005s | ||
+ | sys | ||
+ | araki@cherry: | ||
+ | </ | ||
+ | |||
+ | ==== Rust ==== | ||
+ | === 能書き === | ||
+ | システム記述言語として注目を集めているRust。 | ||
+ | メモリの管理が静的に行われており、予期せぬメモリリークなどによるセキュリティホールなどを言語レベルで防止できる。 | ||
+ | |||
+ | cargoを使用してひな型を作り、getoptsによるオプション処理を加えた。 | ||
+ | ハノイの塔本体部分より、オプションの処理部分のほうが大きい。 | ||
+ | なお、ハノイの塔部分に関しては特にオブジェクト指向的なつくりにはしていない。 | ||
+ | === コード === | ||
+ | コードは、RubyやPythonなどのスクリプト型言語と似た感じになっているが、静的な片付けが行われているところが、これらの言語とは違っている。 | ||
+ | また、ガベージコレクションによる動的なメモリ管理ではなく、静的な管理が中心となる。 | ||
+ | |||
+ | < | ||
+ | use getopts:: | ||
+ | use std:: | ||
+ | use std::env; | ||
+ | |||
+ | fn print_usage(prog_name: | ||
+ | { | ||
+ | let brief = format!(" | ||
+ | print!(" | ||
+ | process:: | ||
+ | } | ||
+ | |||
+ | fn hanoi(n: i32, a: i32, b: i32, c: i32) | ||
+ | { | ||
+ | if n > 0 | ||
+ | { | ||
+ | hanoi(n - 1, a, c, b); | ||
+ | println!(" | ||
+ | hanoi(n - 1, c, b, a); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | fn main() | ||
+ | { | ||
+ | let args: Vec< | ||
+ | let program = args[0].clone(); | ||
+ | |||
+ | let mut opts = Options:: | ||
+ | opts.optopt(" | ||
+ | opts.optflag(" | ||
+ | let matches = match opts.parse(& | ||
+ | { | ||
+ | Ok(m) => { m } | ||
+ | Err(f) => { panic!(f.to_string()) } | ||
+ | }; | ||
+ | if matches.opt_present(" | ||
+ | { | ||
+ | print_usage(& | ||
+ | return; | ||
+ | } | ||
+ | let n = match matches.opt_get_default::< | ||
+ | { | ||
+ | Ok(t) => { t } | ||
+ | Err(f) => { panic!(f.to_string()) } | ||
+ | }; | ||
+ | hanoi(n, 1, 2, 3); | ||
+ | } | ||
+ | </ | ||
+ | === 実行結果 === | ||
+ | < | ||
+ | araki@cherry: | ||
+ | Disc 1 moved from 1 to 3 | ||
+ | Disc 2 moved from 1 to 2 | ||
+ | Disc 1 moved from 3 to 2 | ||
+ | Disc 3 moved from 1 to 3 | ||
+ | Disc 1 moved from 2 to 1 | ||
+ | Disc 2 moved from 2 to 3 | ||
+ | Disc 1 moved from 1 to 3 | ||
+ | Disc 4 moved from 1 to 2 | ||
+ | Disc 1 moved from 3 to 2 | ||
+ | Disc 2 moved from 3 to 1 | ||
+ | Disc 1 moved from 2 to 1 | ||
+ | Disc 3 moved from 3 to 2 | ||
+ | Disc 1 moved from 1 to 3 | ||
+ | Disc 2 moved from 1 to 2 | ||
+ | Disc 1 moved from 3 to 2 | ||
+ | |||
+ | real 0m0.004s | ||
+ | user 0m0.003s | ||
+ | sys | ||
+ | araki@cherry: | ||
+ | </ | ||
いろんな言語でハノイの塔.1561675227.txt.gz · 最終更新: 2019/06/27 22:40 by araki