静岡理工科大学 菅沼ホーム 索引

プログラミング言語の落とし穴

--- C++,Java,JavaScript,PHP,Ruby,Python,C#,VB の比較 ---

 (このページのダウンロード) (目次へ

  プログラムを書く上で,最も重要なことは,誤りを可能な限り 0 にすることです.私が,様々なページで,「分かりやすく,読みやすいプログラムを書く」といったことを強調したのは,それが目的です.しかし,如何に分かりやすいプログラムを書いても,誤りを誘発するような特性を多くの言語が持っていると思っています.ここでは,そのような点について述べていきたいと思います.

  言語によって,全く同じ表現,または,似た表現であるにもかかわらず,その内容,機能が異なったものが多く見受けられます.それらは,各言語のマニュアルの中で詳しく説明されていません.プログラムを別の言語に書き直したい場合,新しく別の言語を学ぶ場合など,これらの点を理解していないととんでもない誤りを起こす可能性があります.さらに,そのような誤りは,最も見つけにくいバグとなって現れます.ここでは,そのような点に重点を置いて,C++ を軸に,Java,JavaScript,PHP,Ruby,Python,C#,及び,VB( Visual Basic ) による基本的なプログラムの書き方について解説します.誤りを導きやすい文法,言語間の違い等に重点を置くため,各言語に対する文法の詳細,組み込み関数等については深く説明しません.このコンテンツにおける説明において不足した部分は,C++ に関しては「 C/C++ と Java 」,Java に関しては「 Java と C/C++ 」,「 ゲームプログラミング」,「 Web プログラミング」,JavaScript に関しては「 ゲームプログラミング」,「 Web プログラミング」,PHP 及び Ruby に関しては「 Web プログラミング」,Python に関しては「 Python と C/C++ 」などを参考にしてください.なお,C#,及び,VB に関する説明は,このページを除いて,私の Web ページにはありません.

  実際,私自身も苦労した経験があります.Java によって作成した時間割作成用のプログラムを,JavaScript 及び C++ に変換しようとした場合です.基本的に,自分自身の不注意が原因ですが,参考のため,その際に行った誤りを,間違って変換してしまった例として挙げておきます.ただし,この例では,クラスを使用していますので,クラスに関する十分な知識がない方は,クラスに対する知識を得てから眺めてみてください.

  いくつかの言語を比較してみて,簡単に感想を述べてみたいと思います.

  1. C++ : 多少分かり難い面があるかもしれませんが,少なくともここでプログラム例としてあげている範囲においては,仕様もしっかりしており,プログラミング上の誤りも犯しにくい言語だと思います.ただし,クラスのオブジェクトを他の変数に代入したり,関数の引数として使用したりした場合,整数型や浮動小数点型などの基本データ型と同様に,すべての値をコピー(浅いコピー)して代入が実行され,または,関数の引数として渡されますが,クラス内で new 演算子を使用していた場合は,アドレスだけがコピーされ,そのアドレスが指す場所はコピーされません.さらに,関数の引数として渡す場合,const 指定しても,アドレスが指すデータは変更可能になってしまいます.プログラミング上の誤りを減少させる目的からも,アドレスが指すデータもコピーされ,かつ,const 指定に対応できるように,仕様を変更すべきだと思います.また,配列だけが,他の配列にコピーして代入できない点も気になります.

      new 演算子に対応する delete 演算子の使用を義務づけるのであれば,ガーベッジコレクション機能も C++ の仕様に付加すべきではないでしょうか.

      小さなことかもしれませんが,同じ文字列でありながら,
    char *str1 = "abc";
    char str2[4] = "abc";
    			
    という定義の違いによる処理方法やその結果の違いが気になります.やはり,どちらかに統一すべきではないでしょうか.

      C++ は,このコンテンツで触れた部分以外に,多くの内容を含んでいます.それらは,かなり分かり難く複雑です.それらの言語仕様の全てを使用しなくても,どのようなプログラムでも書くことができます.基本的に C 言語を使用して,必要に応じて C++ の優れたところを取り入れていく,といった利用方法が良いのではないでしょうか.

  2. Java : C++ 以外の他の言語と同様ですが,他の変数に代入したり,関数の引数として使用したりした場合,整数型や浮動小数点型などの基本データ型と記述方法は全く同じであるにもかかわらず,データ型によって,その結果が異なります.つまり,基本データ型においてはそのデータ自身がコピーされて代入されますが,クラスのオブジェクトなどは,そのアドレスだけがコピーされて代入されているような結果になります.この点は,関数の引数として渡す場合も同様です.プログラミング上の誤りを犯す原因となりかねません.さらに,このような誤りは非常に発見しにくいものとなると思います.何らかの形で,仕様を変更すべきだと思います.

      Java における最大の問題点は,すべてのプログラムをクラス内に記述するようにしたことではないでしょうか.オブジェクト指向,データ駆動型などを念頭に置いた仕様かもしれませんが,手続き型の処理が有効である問題も多く存在しますし,また,現在のコンピュータアーキテクチャが手続き型に向いている点などを考慮すると,考え直した方が良いのではないでしょうか.

      私自身の感覚的な問題かもしれませんが,言語の仕様に対して,開発者の傲慢さを感じることがあります.一つの例は,繰り返しに対するプログラム例に示したような初期設定の問題です.コンパイラは,文法的な誤りは指摘できますが,アルゴリズムの正否の判断は不可能です.それにもかかわらず,アルゴリズム上の問題が無いプログラムに対して,「初期設定がされていない可能性がある」という理由でコンパイルエラーにしてしまうような仕様はいかがでしょうか.当然,警告に止めるべきだと思います.また,未使用の定義済み変数が存在する場合に対して,何のメッセージも出力しませんが,その危険性を考慮すれば,警告メッセージを出力すべきだと思います.

  3. JavaScript : 代入や関数の引数に関する扱いに対して,Java の項で記述した内容と同じことが言えます.また,変数の有効範囲に対しても疑問を感じます.一つは,グローバル変数とローカル変数の扱い方です( let や var を付加するか否か).このままでは,付加することを忘れたような場合,非常に危険です.あと一つは,宣言の巻き上がりに関する問題です.基本的に,変数の有効範囲は,以下のようなルールに従うのが,最も分かりやすいのではないでしょうか.

    • 型宣言(変数に値を代入)した以降で有効になる.
    • 有効範囲をブロック内に限定するか否かを明確にする.
    • 内側のブロックにおいて,外側のブロックで型宣言された変数と同じ名前の変数の型宣言を許すか否かを明確にする.

    少なくとも,同じブロックまたは外側のブロックにおいて,現在の位置より後ろで行われた型宣言の影響が,現在の位置まで及ぶような仕様は避けるべきではないでしょうか.04 行目の代入文は,01 行目で宣言した x が有効であり,問題なしとみるべきではないでしょうか.
    01	let x = 10;
    02	var a = 100;
    03	if (x > 5) {
    04	//	x = 20;   // 許されない(初期設定の前にアクセス)
    05		a = 200;   // 許される
    06		let x = 15;
    07		var a = 300;
    08		   ・・・・・
    			
  4. PHP : 配列を他の変数に代入したり,関数の引数として使用したりした場合,他の言語とは異なり,要素データもコピー(ただし,浅いコピー)されて代入が実行され,または,関数の引数として渡されます.それにもかかわらず,クラスのオブジェクトに関しては他の言語と同様,アドレスによって引き渡されたような結果になります.当然,統一すべきだと思います.しかし,アドレスを使用して配列やクラスのオブジェクトを操作したいような場合も多く存在します.また,実行速度の遅さの原因の一つは,配列の処理方法にあるのではないでしょうか.このような点からも,アドレスを介した処理も可能なようにすべきではないでしょうか.参照を利用することによって,アドレスを使用した場合と似たような処理が可能ですが,先に述べたように,全体的な統一がなされていませんし,かなり独特な処理が行われていますので注意が必要です.

  5. Ruby : 代入や関数の引数に関する扱いに対して,Java の項で記述した内容と同じことが言えます.最大の欠点は,perl のろくでもない仕様を引きずり,組み込み変数として意味の無い記号に様々な意味を与えたり,大文字で始まる変数,@@ で始まる変数,@ で始まる変数など,ここでも,意味の無い記号に対して特別な意味を与えています.Ruby の世界から絶対に出ないような人にとっては良いのかもしれませんが,Ruby に縁の無い人にとっては理解しがたい世界です.このような仕様を設定したことに疑問を感じざるを得ません.また,ブロック付きメソッド呼び出しという機能は,関数名を関数の引数として渡す方法と似ていますが,あまりにも中途半端だと思います(「関数に対するプログラム例」参照).

      小さなことかもしれませんが,同じ文字列でありながら,
    s1 = "abc";   # s1 : "abc"
    s2 = s1;   # s1 : "abc",s2 : "abc"
    s3 = s1;   # s1 : "abc",s2 : "abc",s3 : "abc"
    s2[0..0] = "x";   # s1 : "xbc",s2 : "xbc",s3 : "abc"  s3 は変化しない
    s3       = "ybc";   # s1 : "xbc",s2 : "xbc",s3 : "ybc"  s3 だけが変化
    			
    のような文字列の修正方法による結果の違いが気になります.やはり,どちらかに統一すべきではないでしょうか.

      新しいプログラミング言語について学ぼうとする場合,私の場合,覚えなければ多いとそれだけで意欲を無くしてしまいます.少なくとも,基本的な仕様によって簡単に記述できることを,便利だからといってあえて仕様に含めるような姿勢にはあまり賛成できません.Ruby も,そのような点から,覚えなければいけないことが多すぎるような気がします.少なくとも,perl から引きずったろくでもない仕様を整理すれば,もっとすっきりした言語になると思います.

  6. Python : 代入や関数の引数に関する扱いに対して,Java の項で記述した内容と同じことが言えます.また,main プログラム内の変数を,すべて,グローバル変数として扱う考え方にも疑問を感じます.さらに,多くの人がその拡張に携わったせいか,同じような機能が多くの箇所に現れています.覚えなければならいことが多すぎると共に,統一がとれていません.全体的に見直し,統一がとれた言語にすべきだと思います.新しいプログラミング言語について学ぼうとする場合,私の場合,覚えなければ多いとそれだけで意欲を無くしてしまいます.少なくとも,基本的な仕様によって簡単に記述できることを,便利だからといってあえて仕様に含めるような姿勢にはあまり賛成できません.ただ,字下げを強制的に行わせることは,プログラムの読みやすさの面から考えて,非常に良い考えだと思います.

  7. C#,VB : 代入や関数の引数に関する扱いに対して,Java の項で記述した内容と同じことが言えます.Java に対する項で述べた初期設定の問題に対し,C# では Java と同じ結果になり,VB では 警告メッセージも出しません.いずれも問題だと思います.しかし,未使用の変数に関しては,いずれの言語においても,警告メッセージを出してくれます.また,VB に対しては,論理式の解釈,キーワードの多さ,大文字と小文字の同一視,配列の記述方法や扱い方など,他の言語とあまりにも異なっている点が気になります.

      また,変数の有効範囲( C# VB )には,疑問を感じます.いずれの言語においても,内側のブロックにおいて,外側のブロックで型宣言された変数と同じ名前の変数の型宣言を許していません.この点は問題とは思いませんが,宣言の巻き上がりに関する問題です.なぜ,以下のような文をエラーにしなければならないのでしょうか.05 行目で宣言した変数 y の有効範囲は,あくまで,05 行目以降であるべきです.
    	( C# )
    01	if (x > 5) {
    02		int y = 20;
    03		   ・・・・・
    04	}
    05	int y = 100;   // 2 行目がエラー
    	( VB )
    01	if (x > 5) {
    02		Dim y As Integer = 20;
    03		   ・・・・・
    04	}
    05	Dim y As Integer = 100;   // 2 行目がエラー
    			
      C++ のプログラムを評価するために,gcc( g++ )を使用していますが,少なくとも,このページで扱った範囲においては,VC++ でも,ほとんど同じ結果が得られます.STL を使用した場合,コンパイル時に警告メッセージが出てしまいますが(私には理解不可能),実行可能プログラムは生成され,問題なく動作します.

目次

第1章 言語の種類

第2章 データとその記憶

  1. 2.1 データ型
    1. A. C++
    2. B. Java
    3. C. JavaScript
    4. D. PHP
    5. E. Ruby
    6. F. Python
    7. G. C#
    8. H. VB
  2. 2.2 定数(リテラル),変数,及び,代入
    1. A. C++
    2. B. Java
    3. C. JavaScript
    4. D. PHP
    5. E. Ruby
    6. F. Python
    7. G. C#
    8. H. VB

第3章 簡単なプログラム

  1. 3.1 プログラムの書き方
  2. 3.2 基本的な演算子
    1. 3.2.1 算術演算子
    2. 3.2.2 代入演算子
    3. 3.2.3 インクリメント演算子,デクリメント演算子
  3. 3.3 簡単なプログラム
    1. A. C++
    2. B. Java
    3. C. JavaScript
    4. D. PHP
    5. E. Ruby
    6. F. Python
    7. G. C#
    8. H. VB

第4章 制御文

  1. 4.1 関係演算子,等値演算子,及び,論理演算子
  2. 4.2 分岐
    1. 4.2.1 if 文
      1. A. C++,Java,JavaScript,PHP,C#
      2. B. Ruby
      3. C. Python
      4. D. VB
    2. 4.2.2 switch 文
      1. A. C++,Java,JavaScript,PHP,C#
      2. B. Ruby
      3. C. VB
    3. 4.2.3 分岐に対するプログラム例
      1. A. C++
      2. B. Java
      3. C. JavaScript
      4. D. PHP
      5. E. Ruby
      6. F. Python
      7. G. C#
      8. H. VB
  3. 4.3 繰り返し
    1. 4.3.1 繰り返し文
      1. A. C++,Java,JavaScript,PHP,C#
      2. B. Ruby
      3. C. Python
      4. D. VB
    2. 4.3.2 繰り返しに対するプログラム例
      1. A. C++
      2. B. Java
      3. C. JavaScript
      4. D. PHP
      5. E. Ruby
      6. F. Python
      7. G. C#
      8. H. VB

第5章 配列

  1. 5.1 配列
    1. A. C++
    2. B. Java
    3. C. JavaScript
    4. D. PHP
    5. E. Ruby
    6. F. Python
    7. G. C#
    8. H. VB
  2. 5.2 配列に対するプログラム例
    1. A. C++
    2. B. Java
    3. C. JavaScript
    4. D. PHP
    5. E. Ruby
    6. F. Python
    7. G. C#
    8. H. VB

第6章 関数

  1. 6.1 関数
    1. A. C++
    2. B. Java
    3. C. JavaScript
    4. D. PHP
    5. E. Ruby
    6. F. Python
    7. G. C#
    8. H. VB
  2. 6.2 関数に対するプログラム例
    1. A. C++
    2. B. Java
    3. C. JavaScript
    4. D. PHP
    5. E. Ruby
    6. F. Python
    7. G. C#
    8. H. VB

第7章 クラスと継承

  1. 7.1 クラスと継承
    1. A. C++
    2. B. Java
    3. C. JavaScript
    4. D. PHP
    5. E. Ruby
    6. F. Python
    7. G. C#
    8. H. VB
  2. 7.2 クラスに対するプログラム例
    1. A. C++
    2. B. Java
    3. C. JavaScript
    4. D. PHP
    5. E. Ruby
    6. F. Python
    7. G. C#
    8. H. VB

第8章 変数の有効範囲(スコープ)

  1. A. C++
  2. B. Java
  3. C. JavaScript
  4. D. PHP
  5. E. Ruby
  6. F. Python
  7. G. C#
  8. H. VB

第9章 応用プログラミング

  この章では,いくつかの分野における応用プログラムを紹介します.ただし,入力データに関する説明以外は,原則として,プログラム自身に対する説明は行いません.ほとんどのプログラムは,C/C++ によって記述したプログラムを変換したものです.そのため,デバッグが不十分になっているかもしれません.基本的に,今まで述べた 8 つの言語によって記述したプログラム例を示しますが,一部の例(タイトルの右肩に * を付加したもの)に関しては,JavaScript による記述を行っていません.また,「9.7.3 グラフの表示」に対しては,Java 及び JavaScript による記述だけになります.

  1. 9.1 数値計算
    1. 9.1.1 連立線形方程式,逆行列(ガウス・ジョルダン)
    2. 9.1.2 非線形方程式(二分法)
    3. 9.1.3 非線形方程式(セカント法)
    4. 9.1.4 非線形方程式(ニュートン法)
    5. 9.1.5 代数方程式(ベアストウ法)
    6. 9.1.6 行列の固有値(フレーム法+ベアストウ法)
    7. 9.1.7 実対称行列の固有値・固有ベクトル(ヤコビ法)
    8. 9.1.8 最大固有値と固有ベクトル(べき乗法)
    9. 9.1.9 数値積分(台形則)
    10. 9.1.10 数値積分(シンプソン則)
    11. 9.1.11 微分方程式(ルンゲ・クッタ)
    12. 9.1.12 補間法(ラグランジュ)
    13. 9.1.13 補間法(スプライン)
    14. 9.1.14 補間法(ベジエ曲線)
  2. 9.2 最適化
    1. 9.2.1 最適化(線形計画法)
    2. 9.2.2 最適化(黄金分割法)
    3. 9.2.3 最適化(多項式近似法)
    4. 9.2.4 最適化(最急降下法)
    5. 9.2.5 最適化(共役勾配法)
    6. 9.2.6 最適化( Newton 法)
    7. 9.2.7 最適化(準 Newton 法)
    8. 9.2.8 最適化(シンプレックス法)
    9. 9.2.9 最適化(動的計画法)
    10. 9.2.10 分割法( TSP )*
    11. 9.2.11 逐次改善法( TSP )*
    12. 9.2.12 遺伝的アルゴリズム( TSP,関数の最大値)*
  3. 9.3 確率と統計
    1. 9.3.1 ガンマ関数
    2. 9.3.2 二項分布
    3. 9.3.3 ポアソン分布
    4. 9.3.4 一様分布
    5. 9.3.5 指数分布
    6. 9.3.6 正規分布
    7. 9.3.7 χ2 分布
    8. 9.3.8 t 分布
    9. 9.3.9 F 分布
    10. 9.3.10 乱数の発生
  4. 9.4 待ち行列
    1. 9.4.1 待ち行列(簡単な例)
    2. 9.4.2 待ち行列(複雑な例)
  5. 9.5 多変量解析
    1. 9.5.1 最小二乗法
    2. 9.5.2 重回帰分析
    3. 9.5.3 正準相関分析
    4. 9.5.4 主成分分析
    5. 9.5.5 因子分析
    6. 9.5.6 クラスター分析
    7. 9.5.7 分散分析
  6. 9.6 ニューラルネットワーク
    1. 9.6.1 Hopfield ネットワーク
    2. 9.6.2 パーセプトロン
    3. 9.6.3 Winner-Take-All
    4. 9.6.4 競合学習
    5. 9.6.5 バックプロパゲーション
  7. 9.7 その他
    1. 9.7.1 ボード線図
    2. 9.7.2 ファジイ推論
    3. 9.7.3 グラフの表示( Java,JavaScript )

静岡理工科大学 菅沼ホーム 索引