読者です 読者をやめる 読者になる 読者になる

akimachoのはてなブログ

ICTとデザインのためのブログ

リファクタリングメモ - アサーションの導入 Introduce Assertion

リファクタリング技術の一つである「アサーションの導入」のメモです。

主に、結城 浩,"Java言語で学ぶリファクタリング入門"の第3章を参考にしました。

なぜアサーションが必要なのか

暗黙の了解みたいなものをコード中に書いたとしても、実行されるプログラムの振る舞いを拘束するものではありません。 つまり、プログラマの意図したとおりに必ずしも成り立つとは限らないわけです。

そんなわけでアサーションを導入することで前提条件をソースコードとして表明して、その条件をみたしているかを処理系に教えてもらうようにします。

アサーションに関しては"達人プログラマー"でも"表明プログラミング"という節で触れられています。

もし起こり得ないというのであれば、表明を用いてそれを証明すること p.124

思い込みや決め付け、焦り、疲れなど様々なバイアスが開発者にかかるものですが、コードとして表明可能なものは書いておくことでできるかぎり恣意的な要素を取り除こうよというわけですね。

余談ですが、思い込みで決めつけてしまいそうなこととして以下のようなものが紹介されています(演習問題19)。詳しくは"達人プログラマー"で!

  • 一ヶ月(標準的なグレゴリオ暦を使用)が28日よりも少ない
  • stat(".", &sb) == -1となる
  • C++において、a=2;b=3;if(a+b != 5) exit(1);でexit(1)となる
  • 三角形の内角の和が180度でない
  • 1分が60秒にならない
  • Javaにおいて、(a+1) <= aとなる

達人プログラマー―システム開発の職人から名匠への道

達人プログラマー―システム開発の職人から名匠への道

  • 作者: アンドリューハント,デビッドトーマス,Andrew Hunt,David Thomas,村上雅章
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2000/11
  • メディア: 単行本
  • 購入: 42人 クリック: 1,099回
  • この商品を含むブログ (350件) を見る


アサーションの注意事項


Java+eclipseにおけるアサーション

Java言語においてはassertはキーワード扱いされます。

アサーションの式評価がfalseの場合、Java処理系は例外java.lang.AssertionErrorを投げます。

eclipseにおいてはアサーションがそのまま実行されません。

Run → Run Configurations → Arguments → VM arguments: に以下のオプション引数を追加することで、assertによるチェックを有効にします。

f:id:akimacho:20160725212948p:plain

f:id:akimacho:20160725212959p:plain

C

マニュアルを読むと、式が成り立たない場合abort(3)を呼び出してプログラムを終了させます。

また、assertは関数ではなくマクロとして定義されており、assertを除去するにはコンパイルオプションに-DNDEBUGを加えます

#include <assert.h>

assert(expression);

PHP5

bool assert ( mixed $assertion [, string $description ] )

関数として実装されています。

PHP7からは言語構造(つまりキーワード?)として組み込まれるようになって、Production環境におけるアサーションの除去ができるようになったみたいです。

以下、psyshによる実行結果

>>> assert(1 != 2)
=> true
>>> assert(1 == 2)
PHP warning:  assert(): Assertion failed on line 1

Python

Python2.xとPython3.xともに関数ではなくキーワードとして組み込まれています。

アサーションの除去には、最適化を行う-Oオプションを加えます

#!/usr/bin/env python

assert 1 == 1
assert 1 != 1

以下実行結果。

% python assertion.py
Traceback (most recent call last):
  File "assertion.py", line 2, in <module>
    assert 1 != 1
AssertionError
% python -O assertion.py
% ← 何も表示されない

Swift

Swift2.xおよびSwift3.xでは関数扱いのようです。

他の言語もそうですが、Swiftにはアサーションを行う関数が複数用意されています。

func assert(_: @autoclosure () -> Bool, _: @autoclosure () -> String, file: StaticString, line: UInt)
func assertionFailure(_: @autoclosure () -> String, file: StaticString, line: UInt)
func precondition(_: @autoclosure () -> Bool, _: @autoclosure () -> String, file: StaticString, line: UInt)
@noreturn func preconditionFailure(_: @autoclosure () -> String, file: StaticString, line: UInt)
@noreturn func fatalError(_: @autoclosure () -> String, file: StaticString, line: UInt)

assert(...)の除去は、最適化オプション-Oを追加することで除去されます。これのオプションは、XCodeiOSアプリをリリースする際にデフォルトで追加されます。

逆に、-Ononeを追加することで最適化が行われず、さきほど挙げた関数はすべて実行されることになります。


おわりに

今までなんとなくアサーションを書いていましたが、今回勉強してアサーションの知識が整理されました。

Java言語で学ぶリファクタリング入門

Java言語で学ぶリファクタリング入門