#!/usr/bin/env python3
# coding: utf_8
# bintree.py
import sys
import random
import math
# import kwargs as kw
## constants
DEFAULT_PEN_WIDTH=0.25
#parameters
verbose=True
debug=False
# # A template for docstring
# Parameters
# ----------
# name: type
# Returns
# ----------
#=====
#ヘルパー関数
#=====
[docs]def log(msg, outs=sys.stderr):
"""標準関数print(msg)のラッパー.出力ストリームoutsを与えるとそこへ,指定がなければstd.errへ,print(msg)の出力を書き込む.
Args:
msg (str) : 出力する文字列
outs (出力ストリーム) : 出力先.default=sys.stderr.
"""
print(f'{ msg }', file=outs)
[docs]def panic(msg, outs=sys.stdout):
"""メッセージ文字列を標準出力に印刷して,例外を投げて実行を中止する.
Args:
msg (str): メッセージ文字列
outs (outstream): 出力ストリーム.デフォールトはsys.stdout.
"""
print(f'@panic!: {msg}', file=outs)
raise Exception("panic!")
[docs]def ensure(test, msg=None, outs=None):
"""ブール式test を評価し,真ならばTrueを返し,偽ならば,panic関数を呼んで,メッセージ文字列を出力して実行を中止する.
Args:
test (ブール式): テストのためのブール式
msg (str): メッセージ文字列
outs (outstream): 出力ストリーム.デフォールトはsys.stdout.
"""
if test:
return test
else:
panic(f'@ensure: {msg}', outs=outs)
[docs]def report_module_path(name):
log(f'@printing paths in module:{ name }...')
for d in sys.path:
log(f' - { d }')
log(f'@paths end')
[docs]def ensure_defined(value=None, default=None, required=False):
"""空でなければ,値valueをそのまま返す.代入時に,変数の値が空でないことを保証するために用いる.
* 空のときに,フラグ`required==True`ならば即時にエラーを投げて停止する.
* そうでないときに,非空のデフォールト値`default`が与えられていれば,それを返す.
Args:
value (Any) : 値
default (Any) : デフォールト値
required (bool) : 値が非Noneであることを要請するフラグ.
"""
if value==None:
if required==False and default != None:
return default
else:
panic('ensure_defined: value={value} is required and default={default}!:')
else:
return value
##=====
## 便利関数:型チェック
##=====
[docs]def elemtype_normalize(etype=None):
"""与えられた入力オブジェクトetypeが,正しい型オブジェクトかを検査し,
型リストetypes_を返す.もしそうでなければ,エラーを投げて異常終了する.
ただし,etype==Noneのときは,そのまま返す(任意の型を表す).
Args:
etype (Object) : 基礎型,または,基礎型のリスト(和)
Returns:
(list(型)) : 正規化された型リスト
"""
if etype == None:
return etype
ety_list = None ## 作業用:a list for a union type
if isinstance(etype, type): ## primitive type
ety_list = [etype]
elif isinstance(etype, tuple): ##union type
ety_list = []
for idx, ty in enumerate(etype):
if isinstance(ty, type):
ety_list.append(ty)
else:
panic(f'common.is_sequence: the {idx}-th element "{ ty }"'+
f' of etype must be a type object!')
pass
else:
panic(f'common.is_sequence: etype="{etype}"'+
f' must be a type object!')
return ety_list
def _normalize_elemtype(etype=None):
"""関数is_typeof_value()の補助関数.受け取った型または型リストelemtypeを,型リストに変換して返す.
"""
etypes_ = None ## 作業用:a list for a union type
# if etype == None:
# panic(f'normalize_etype: etype must be non-None!')
ensure_defined(etype, required=True)
if isinstance(etype, type): ## primitive type
etypes_ = [etype]
elif isinstance(etype, tuple): ##union type
etypes_ = []
for idx, ty in enumerate(etype):
if isinstance(ty, type):
etypes_.append(ty)
else:
panic(f'normalize_etype: the {idx}-th element "{ ty }" of etype must be a type object!')
else:
panic(f'normalize_elemtype: etype="{elemetype}" must be a type object!')
return etypes_
[docs]def is_typeof_seq(L, etype=None, dim=None, verbose=False):
"""オブジェクトLが,指定された型と長さをもつ系列オブジェクトかを真偽で返す.
さらに系列型であり,etypeがNoneでないときに,系列の全ての要素が型etypeをもつならば`True`,そうでなければ`False`を返す.
Args:
L (object) : オブジェクト
etype (Type) : int, float, strなどのprimitive型,または,型の選言(union etype)を表すリスト `(ty1, ..., tyN)`.
length (int) : 系列が満たすべき長さ
verbose (bool) : ログ出力のスイッチ
Returns:
(bool) : オブジェクトLが,etypeの型指定を満たす系列型ならば`True`を, そうでなければ`False`を返す.
Example::
>>> com.is_typeof_seq([1,2,3], etype=int)
True
>>> com.is_typeof_seq([1,2.0,3], etype=int)
False
>>> com.is_typeof_seq([1,2.0,3], etype=(int, float))
True
>>> com.is_typeof_seq([1,'b',3], etype=int)
False
>>> com.is_typeof_seq([1,'b',3], etype=(int, str))
True
"""
## 型引数のテスト
etypes_ = elemtype_normalize(etype)
## 系列型かどうかをテスト
if isinstance(L, tuple) or isinstance(L, list):
isSeq = True
elif hasattr(L, '__iter__'):
## iterableかどうかを判定する標準の書き方
isSeq = True
else:
isSeq = False
##長さテスト
if isSeq and dim!=None and len(L) != dim:
if verbose: print(f'warning: common.is_sequence: it must have dim={dim} but len={len(L)}!')
return False
if not isSeq:
if verbose: print(f'warning: common.is_sequence: the object L is not of sequence type!: L={L}')
return False
else: ## isSeq==True
## もしtypeが与えられていたら,さらに要素型をテストする
if etypes_ == None:
return True
else:
for idx, elem in enumerate(L):
res_ = False
for ty in etypes_:
if isinstance(elem, ty):
res_ = True
break
if not res_:
if verbose: print(f'warning: common.is_sequence: the {idx}-th element "{elem}" does not satisfies etype "{etype}" in the sequence {L}!')
return False
## 全ての要素が要素型を満たした
return True
[docs]def is_typeof_value(obj, etype=None):
"""オブジェクトobjが空でなく,型etypeをもつとき,`True`を返し,それ以外のとき`False`を返す.
Args:
obj (object) : オブジェクト
etype (Type) : 型のリスト`(ty1, ..., tyN)`.
Returns:
(bool) : オブジェクトobjが,etypeの型指定を満たすならば`True`を, そうでなければ`False`を返す.
"""
## 引数テスト: 型リストに正規化する.単一型tは,単一元リスト(t)とする.
etypes_ = _normalize_elemtype(etype=etype)
## 要素型をテストする
for ty in etypes_:
if isinstance(obj, ty):
return True ##型tyを満たした
return False ##どの型も満たさない
##=====
## 便利関数:型チェック
##=====
def _simple_type_name(etype=None):
# ensure(etype!=None, f'etype={None} must be defined!')
ensure_defined(etype, required=True)
if not isinstance(etype, tuple):
return f'{etype.__name__}'
else:
s = f'tuple('
for idx, ty in enumerate(etype):
if idx > 0: s += ', '
s += f'{_simple_type_name(etype=ty)}'
return s + ')'
[docs]def ensure_value(value=None, default=None, etype=None, nullable=True, name='it', typename=None, to_check_only=False):
"""入力として受け取った値`value`が指定された要素型`etype`の値であるかを検査し,条件を満たせば値をそのまま返す.もし指定の条件を満たさなければ,エラーを投げて終了する.
* 空でなければ,値valueをそのまま返す.
* 空のときに,フラグ`required==True`ならば即時にエラーを投げて停止する.
* そうでないときに,非空のデフォールト値`default`が与えられていれば,それを返す.
Args:
value (Any) : 値
etype (tuple(type)) : 型のタプル.型の選言を表す.デフォールト値`etype=None`.
default (Any) : 任意のデフォールト値を与える.値がNoneのとき返される.
nullable (bool) : 値がNoneでも良いことを表すフラグ.nullable=Trueかつ値がNullならばエラーを投げる.
to_check_only (bool) : もし真ならば,型を満たさない時は,エラーを投げずに実行されて,Noneを返す.default is False.ここに,to_check_onlyが真ならば,`nullable==False`および`default = None`に上書きされるので注意.
Notes:
データの検査を行う関数の実装用に用いる.典型的な使用例は,次の通り:
* 変数の値を保証して代入するためのフィルタの実装
* 値の型を保証した代入
Note:
引数`etype`の組の要素として,`None`の型を与えたい時は,要素型として`type(None)`を与えること.
"""
ensure(etype!=None, f'etype={etype} must be defined!')
#判定関数に用いる場合の前処理
if to_check_only:
nullable = False
default = None
if typename==None:
typename=_simple_type_name(etype)
#値が空な場合の処理
if value==None:
if nullable==True:
if default != None:
return default
else:
return None
else:
if to_check_only: return None
else: com.panic(f'{name}={value} must be {typename}! case 1: value(={value})==None while nullable={nullable}')
#指定された型の値の対か?
if is_typeof_value(value, etype=etype):
return value
else:
if to_check_only: return None
else: com.panic(f'{name}={value} must be {typename}! case 2: value={value} is not a sequence of specified type={etype}')
[docs]def ensure_vector(value=None, dim=2, default=None, etype=None, nullable=True, name='it', typename=None, to_check_only=False):
"""入力として受け取った値`value`が指定された要素型`etype`と要素数`dim`をもつ数値の組であるかを検査し,条件を満たせば値をそのまま返す.もし指定の条件を満たさなければ,エラーを投げて終了する.
* 空でなければ,値valueをそのまま返す.
* 空のときに,フラグ`required==True`ならば即時にエラーを投げて停止する.
* そうでないときに,非空のデフォールト値`default`が与えられていれば,それを返す.
Args:
value (Any) : 値
dim (int) : ベクトルの長さ
etype (tuple(type)) : 型のタプル.型の選言を表す.デフォールト値`etype=None`.
default (Any) : 任意のデフォールト値を与える.値がNoneのとき返される.
nullable (bool) : 値がNoneでも良いことを表すフラグ.nullable=Trueかつ値がNullならばエラーを投げる.
to_check_only (bool) : もし真ならば,型を満たさない時は,エラーを投げずに実行されて,Noneを返す.default is False.ここに,to_check_onlyが真ならば,`nullable==False`および`default = None`に上書きされるので注意.
Notes:
データの検査を行う関数の実装用に用いる.典型的な使用例は,次の通り:
* 変数の値を保証して代入するためのフィルタの実装
* 値の型を保証した代入
Note:
引数`etype`の組の要素として,`None`の型を与えたい時は,要素型として`type(None)`を与えること.
"""
ensure(etype!=None, f'etype={etype} must be non-None!')
#判定関数に用いる場合の前処理
if to_check_only:
nullable = False
default = None
if typename==None:
typename=_simple_type_name(etype)
#値が空な場合の処理
if value==None:
if nullable==True:
if default != None:
return default
else:
return None
else:
if to_check_only: return None
else: com.panic(f'{name}={value} must be {typename}! case 1: value(={value})==None while nullable={nullable}')
#指定された型の値の対か?
if not is_typeof_seq(value, etype=etype):
if to_check_only: return None
else: com.panic(f'{name}={value} must be {typename}! case 2: value={value} is not a sequence of specified type={etype}')
elif len(value) != dim:
if to_check_only: return None
else: com.panic(f'{name}={value} must be {typename}! case 3: value={value} has wrong length where len(value)(={len(value)})==dim(={dim})')
else:
return value
[docs]def ensure_point(value=None, name=None, nullable=True, default=None, etype=None, to_check_only=False):
"""値が指定された型の点(数の対)かどうかを検査し,値valueをそのまま返す.
条件を満たさなければ,エラーを投げて終了する.
関数`ensure_vector`のラッパー.
* 空のときに,フラグ`required==True`ならば即時にエラーを投げて停止する.
* そうでないときに,非空のデフォールト値`default`が与えられていれば,それを返す.
Args:
value (Any) : 値
nullable (bool) : 値がNoneでも良いことを表すフラグ.
default (Any) : 任意のデフォールト値を与える.値がNoneのとき返される.
etype (tuple(type)) : 型のタプル.型の選言を表す.数値ベクトルは,デフォールトの`etype=(int, float)`で良い.
to_check_only (bool) : もし真ならば,型を満たさない時は,エラーを投げずに実行されて,Noneを返す.default is False.ここに,to_check_onlyが真ならば,`nullable==False`および`default = None`に上書きされるので注意.
"""
if etype==None:
etype = (float,int) #要素型のdefaultは,数値型
typename='a point'
else:
typename=_simple_type_name(etype)
return ensure_vector(value=value, default=default,
etype=etype, nullable=nullable,
dim=2, name=name, typename=typename,
to_check_only=to_check_only)
[docs]def ensure_box(value=None, name=None, nullable=True, default=None, etype=None, to_check_only=False):
"""値が指定された型の点(数の対)かどうかを検査し,値valueをそのまま返す.
条件を満たさなければ,エラーを投げて終了する.
関数`ensure_vector`のラッパー.
* 空のときに,フラグ`nullable==False`ならば即時にエラーを投げて停止する.
* そうでないときに,非空のデフォールト値`default`が与えられていれば,それを返す.
Args:
value (Any) : 値
etype (tuple(type)) : 選言型のタプル.defaultは(float,int).
nullable (bool) : 値がNoneでも良いことを表すフラグ.
etype (tuple(type)) : 型のタプル.型の選言を表す.数値ベクトルは,デフォールトの`etype=(int, float)`で良い.
default (Any) : 任意のデフォールト値を与える.値がNoneのとき返される.
to_check_only (bool) : もし真ならば,型を満たさない時は,エラーを投げずに実行されて,Noneを返す.default is False.ここに,to_check_onlyが真ならば,`nullable==False`および`default = None`に上書きされるので注意.
"""
if etype==None:
etype = (float,int) #要素型のdefaultは,数値型
typename='a point'
else:
typename=_simple_type_name(etype)
return ensure_vector(value=value, default=default,
etype=etype, nullable=nullable,
dim=4, name=name, typename=typename,
to_check_only=to_check_only)
##=====
## 便利関数:コマンドライン入力
##=====
#未使用
[docs]def parse_opt_shape(str_shape=None, default_shape=None):
"""shape文字列str_shape=='m:n'をパーズしてshape = (m,n)を返す
"""
if str_shape:
shape0 = str_shape.split(':')
ensure(len(shape0)==2, 'shape={mn} must have two numbers!')
shape = [0,0]
for i in range(2):
shape[i] = int(shape0[i])
return shape
else:
return default_shape
#未使用
[docs]def parse_opt_width(width=None):
if width:
return width
else:
return DEFAULT_PEN_WIDTH #基底描画システムのデフォールト値
#未使用
[docs]def get_cyclic(elements=None, idx=0):
"""非負整数idxを受け取り,長さnの配列の`(idx % n)`番目の要素を返す.
Args:
elements=None (list(object))
idx=0 (int)
Returns:
object: 選択された要素
Notes:
どのように大きなidxに対しても,添字エラーにならず何かの要素を返す.
"""
return elements[idx % len(elements)]
##=====
## ヘルパー関数:
##=====
##=====
## EOF
##=====