前回までの「Python初体験」の続きです。

今回はユーザー関数の定義(前回ちょっと出ましたが...)をやります。
基本的な構文構造は、以下の通りです。
"""\
#"def"は関数定義(definition)の宣言キーワードですね。引数に型宣言が無いのが新鮮です。
def 関数名(引数名1, 引数名2, ...):  #またもや最後に":"が必須です。
    処理1  #またもや空白文字4文字の字下げ(インデント)でブロックを表します。
    処理2  #中に条件式やループを入れても一向に結構です。
    ...\
    return 戻り値  #関数が戻り値を返す場合、このようにreturn分を使います。
"""

今回の「Python初体験」の関数定義の例題はくだらなかったので、自分で作ってみました。


(1)前回の10進整数チェック関数と、それを改造した10進実数チェック関数の他、
(2)引数の二乗と階乗の関数を定義し、後者は再帰処理で書きます。
(3)一番関心があったのは、引数に型宣言が無いので、自動判別で整数も実数も同じ関数で処理できるか否か?でした。
(4)結果はご覧あれ。


"""\
#文字列が10進整数かどうかをチェック
def isint(s):  #10進整数かどうかを判定
    try:
        int(s, 10)  # 文字列を実際にint関数で変換してみる
    except ValueError:
        return False
    else:
        return True
 

#文字列が10進実数かどうかをチェック
def isfloat(s):  #10進実数かどうかを判定
    try:
        float(s)  # 文字列を実際にfloat関数で変換してみる
    except ValueError:
        return False
    else:
        return True
 

#引数の二乗を返す
def squared(arg):
    return arg * arg
 

#引数の階乗を返す
def factorial(arg):
    val = 1
    if arg > 1:
        val = arg * factorial(arg - 1) 
    return val

str = input("値(数字)を入力してください")
if isint(str):
    num = int(str)
elif isfloat(str):
    num = float(str)
else:
    num = None
if num is None or num == 0:
    print("入力されたものは数字ではありません。")
else:
    print("入力された数字の二乗は", squared(num), "です。")
    print("入力された数字の階乗は", factorial(num), "です。")\
"""

【出力】<整数>
値(数字)を入力してください6
入力された数字の二乗は 36 です。
入力された数字の階乗は 720 です。
【出力】<実数>
値(数字)を入力してください6.0
入力された数字の二乗は 36.0 です。
入力された数字の階乗は 720.0 です。

やるもんだね、Python!

ps. 一旦本記事をアップしてから、「なんで、『やるもんだね』なんだっけ?」と思い返し、

Pythonは引数に型宣言が無く、整数でも、実数でも(↑の処理では無理ですが、エラーを出さずに処理が可能ならば、たとえ文字列でも)、なんでも渡せる

ところが「やるもんだね」の正体だったと思いだしました。深謝。

 

前回までの「Python初体験」の続きです。

今回はPythonのループ(反復)処理です。Pythonでループ処理を行うにはwhile文とfor文しかないようですが、for文が柔軟で配列等()を利用すると様々なループ文を書くことが出来るようです。
:標準で利用できる配列にはリスト(list)型、タプル(tuple)型、辞書(dict)型の他、集合(要素を波括弧{}で定義するset型オブジェクト)がありますが、その他ライブラリーで提供されるNumPy配列も利用できるそうです。

最初はWhileから説明がありました。条件式と同じく、Pythonの構文では、
"""\
while 条件式: #':'は必須
    処理1    #ブロックは空白4文字の字下げ(インデント)を行う
    処理2
    ・
    ・
    if 条件式A:  #特定条件でループを抜け出すには
        break    #break文を使います。
    if 条件式B:  #特定条件で以降の処理を行わずにループを繰り返すには
        continue #continue文を使います。
    if 条件式C:  #特定条件で何も処理を行わずにループを続けるには
        pass     #pass文を使います。(何も処理を書かないと構文エラーになる)
    ・
    ・
    ・
#ブロックを抜けたら、インデントを戻す\
"""
となります。passはC系言語にはなく、";"(空文)に相当しますね。

一方、for文は結構変わっていて、(以下「カウンター変数」等日本語は説明語です。)
"""\
for カウンター変数 in range(開始値, 終了値, 増減値):
    処理1    #ブロックは空白4文字の字下げ(インデント)を行う
    処理2
    ・
    ・
    if 条件式A:  #特定条件でループを抜け出すには
        break    #break文を使います。
    if 条件式B:  #特定条件で以降の処理を行わずにループを繰り返すには
        continue #continue文を使います。
    if 条件式C:  #特定条件で何も処理を行わずにループを続けるには
        pass     #pass文を使います。(何も処理を書かないと構文エラーになる)
    ・
    ・
    ・
#ブロックを抜けたら、インデントを戻す\
"""
のように書くそうです。例えば、


for i in range(10):  #と書くと変数iは0から9まで一つずつ増加します。
    処理
    ・
    ・
    ・
#ブロックを抜けたら、インデントを戻す\
 

for i in range(4, 12):  #と書くと変数iは4から11まで一つずつ増加します。
    処理
    ・
    ・
    ・
#ブロックを抜けたら、インデントを戻す\
 

for i in range(2, 9, 3):  #と書くと変数iは2から8まで3ずつ増加(2, 5, 8が該当)します。
    処理
    ・
    ・
    ・
#ブロックを抜けたら、インデントを戻す\

テストの為に、次のwhileとforを使ったプログラムを走らせてみました。
"""\

#【while:テスト】

#文字列が10進整数かどうかをチェック
def isint(s):  #10進整数かどうかを判定
    try:
        int(s, 10)  # 文字列を実際にint関数で変換してみる
    except ValueError:
        return False
    else:
        return True
#注:赤字の部分はまだ習っていませんが、C++やC#でいうところの"try() { } catch() { } finally() { }のようです。

while True:  #無限ループですね。
    str = input("0から9までのキーを使って10進数字を入力してください。(Enterのみで終了します。)")
    if str == "":  #""は空文(strの指すメモリー領域はあるが、データが無い・・・Pythonにはヌル終端もない)を意味します。
        break;
    elif isint(str):  #整数か否かのチェック関数
        print("入力された文字は:", str, "です。")
    else:  #上記以外-エラー処理
        print("入力された文字は数字ではありません。")
print("終了しました。")

#【for:テスト】

print("for i in range(5):")
for i in range(5):
    print("iの値は", i)
 

print("for i in range(4, 20, 5):")
for i in range(4, 20, 5):
    print("iの値は", i)
 

print("for i in range(15, 8, -2):")
for i in range(15, 8, -2):
    print("iの値は", i)
arr = [1, 3, 5, 7, 9]
 

print("for i in arr:  #arr = [1, 3, 5, 7, 9]")
for i in arr:
    print("iの値は", i)\
"""

この結果は次の通りでした。

"""\
0から9までのキーを使って10進数字を入力してください。(Enterのみで終了します。)25
入力された文字は: 25 です。
0から9までのキーを使って10進数字を入力してください。(Enterのみで終了します。)
終了しました。
for i in range(5):
iの値は 0
iの値は 1
iの値は 2
iの値は 3
iの値は 4
for i in range(4, 20, 5):
iの値は 4
iの値は 9
iの値は 14
iの値は 19
for i in range(15, 8, -2):
iの値は 15
iの値は 13
iの値は 11
iの値は 9
for i in arr:  #arr = [1, 3, 5, 7, 9]
iの値は 1
iの値は 3
iの値は 5
iの値は 7
iの値は 9\
"""

ではまた次回。

 

また、どうでもよい話でスミマセン。

 

私、元は32bitのCore i7のWin 10マシンを使っていました。その時のブラウザーはGoogle Chromeで、軽快感が気に入っていました。その後、64bitのRyzen 7のWin 11マシンに乗り換えた際に、ストーレージが1Tから500Gに落としたので、素直にMicrosoft Edgeを使うようになりました。(ちょっと鈍重感は感じていましたが、実用上の問題はなかったので...)

 

今朝、Microsoft Edgeを立ち上げたら、ストンと落ちます。

 

ん?

 

再度立ち上げ、「ページを復旧します。」

 

んん?

 

また落ちるじゃないですか?

 

再々度立ち上げ、「ページを復旧します。」

 

んんん?

 

また落ちるじゃないですか?

 

再々々度立ち上げ、「ページを復旧します。」...を繰り返します。

 

5回へくったら、殺すっ!

 

そして再々々々々度立ち上げ。

 

すっとん

 

てめぇ~(ずごごごごごごご.....怒りの波動)

 

ということで、電光石火でお気に入りをエクスポート、Chromeをダウンロード、インストール、お気に入りインポート、再設定完了。

 

あー。すっきりしたぁ。

 

ps. ついでにEdgeとEdgeのUpdateプログラムも削除してやろうと思いましたが、システム密着型でできませんでした。残念!!!

前回までの「Python初体験」の続きです。

条件式を論理演算子and、or、notでつなぐだけですので、やや退屈ですね。
ただし、not(反転)を整数変数に与えるとどうなるかは興味があったので実験してみました。結果は(論理演算なのか)単にbool型として処理されるようです。ビット演算子の~(NOT・反転)との違いを表示するようにしました。

# andサンプル
age = 10   # 例として、10歳 身長120cmとする
height = 120
if 10 <= age and 120 <= height:  # 例題では()で条件式が括ってあったが、無くても演算子順位から正常に動く
    print("お乗りいただけます")
else:
    print("ご遠慮ください")
 

# orサンプル
age = 90   # 年齢を90歳とする
if (age <= 10) or (80 <= age):  # ()はなくともよい
    print("ご遠慮ください")
else:
    print("お乗りいただけます")

 

# notサンプル
age = 20  # 年齢は20歳
if not (age < 10):
    print("お乗りいただけます。")  #さて、問題です。(not age < 30) はTrueでしょうか?Falseでしょうか?

                                               #(not age)が0なら、0 < 30はTrue、(age < 30)がTrueなら、not TrueはFalse


a = 10  # notを使うと、aの値が何であれ、0以外は0、0なら1になる。
b = not a
print("aの2進数値は", bin(a), "で、bの2進数値は", bin(b), "です。")

#(出力)aの2進数値は 0b1010 で、bの2進数値は 0b0 です。not ageも0になりますよ。

 

b = ~a  # bは0b00001010なので、aは0b11110101になるはず(実際には32bit整数、64bit整数を使っていると考えられ、この8bitに更に+24bit、+56bitの'0'と'1'が付加される。以下同じ。↓の「2の補数表現を利用する方法」参照)
print("aの2進数値は", bin(a), "で、bの2進数値は", bin(b), "です。")  # bが符号付整数になる為、実際には0b11110101が-0b1011になる。このように0b11110101が-0b1011になることを2の補数表現(0b11110101 + 0b00001011 = 0 ∴ 0b11110101 = -0b00001011)という。

#(出力)aの2進数値は 0b1010 で、bの2進数値は -0b1011 です。


b = ~a & 0b00001111  # bの下4桁だけを残すために0b00001111でマスクする。
print("aの2進数値は", bin(a), "で、bの2進数値は", bin(b), "です。")  # 頭の0が取れて、0b101となる。
#(出力)aの2進数値は 0b1010 で、bの2進数値は0b101です。

いかがだったでしょうか?PCが8bit、16bit、32bit、64bitと発展してきたので、整数の2進数表現も、

0b00000000(8bit-16進数表現0x00)
0b0000000000000000(16bit-16進数表現0x0000)
0b00000000000000000000000000000000(32bit-16進数表現0x00000000)
0b0000000000000000000000000000000000000000000000000000000000000000(64bit-16進数表現0x0000000000000000)
になってしまい(反吐が出そうですが)、プログラミング言語の内部表現を考えるにはよい材料ですね。

ps. Pythonには符号なし整数は無いのかと調べてみた所、どのwebサイトでも次のCopilotの回答のような答えを得ました。
Pythonには組み込みの符号なし型は存在しません。
しかし、符号付き整数を符号なし整数に変換する方法はいくつかあります。以下にいくつかのアプローチを示します。
(1)2の補数表現を利用する方法
Pythonの整数は、無限に広い2の補数表現を使用しているように見えます。この方法は、2の補数表現を念頭に置いています。具体的には、符号なし32ビット整数を得るために、整数に対して (unsigned long) を適用し、次に 2**32 を負の値に追加します。例えば、次のようにします。
i = -6884376
unsigned_value = (unsigned long)(i) + 2**32
この方法は、Pythonの整数が2の補数表現を使用していることを前提としています。

 

(2)ビット単位の演算を利用する方法
Pythonのビット単位の演算子は、
2の補数の値で動作します。符号付き整数を符号なし整数に変換するには、適切なビットマスクを使用します。(32ビットの場合:)
i = -6884376
unsigned_value = i &
0xffffffff
または、64ビットの場合:
i = -6884376
unsigned_value = i &
0xffffffffffffffff

(3)ctypesモジュールを利用する方法
ctypes モジュールを使用して、プラットフォームCコンパイラの機能を複製することもできます。

import ctypes
i = -6884376
unsigned_value = ctypes.c_ulong(i).value

この方法は、Cの unsigned long と同等の値を取得します。」

 

ps of ps. 私が初めてSONYの「MSX(8bit パソコン)」で機械語を習った時には、2進数、16進数変換を次のように覚えました。

 

(2進数) 0000→1から4の位を「いち(1)、にー(2)、よん(4)、ぱー(8)」の位として覚える

     1001→8+1=9(十進法)

     1111→8+4+2+1=15(十進法)

(16進数)00→0からF(十進数の15-0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F)

      09→9(十進法)

      0F→15(十進法)

(2進数→16進数変換)

11111011→上位1111は8+4+2+1=15(十進法)なのでF

  ↓   ↓   下位1011は8+2+1=11(十進法)なのでB

  F   B

 

まっこと、長閑だったですねぇ。

ps of ps of ps. 問題の答え。矢張りnotの演算順位は算術演算記号よりも低いために not age < 30はFalse("not (age < 30)"と等価)になりました。

プログラミングではなく、無駄話ばかりですみません。

今日は標記のイアフォーンを初めて使ったのでそのインプレなど。

 

私は車の運転中と早朝のウォーキング中は音楽を聴く習慣があり、iPhoneの中は1960~80年代の「好みの懐メロ()」楽曲で詰まっています。

:1960年代のイタリア映画のサウンドトラックから新し目のJ-POPまで、雑多、多種多様ですが、一番厚いのがSmooth Jazz(昔のCrossoverやFusion)って感じですか。

 

そんなわけでイアフォーンはコード式からワイアレス迄、いくつも持っていますが、そろそろみんな古くなったので新しいのを導入しようと考え、「骨伝導はどうなったかな?」ということでググってみました。

 

矢張りピンキリあるのですが、先ずはどんなものかお試し、ということで安めの奴(注)を一つ購入し、一昨日届きましたが、翌日は生憎の雨で、初装着が今朝になったということです。

注:買った後の今頃調べたのですが、「Shokz(ショックス)」というブランドのパチモンっぽいですね。

 

まず装着する訳ですが、(私のは一体型でしたので分かり易く)フレームを後頭部側にして、湾曲部を耳(耳介-ジカイ)の上部に引っ掛けます。すると骨伝導イヤホーン/スピーカーが蟀谷(コメカミ)あたりに来ます。その状態で電源ボタンを長押ししてオンにすると、電源が入った、ペアリングを開始/終了(というよりも「完了」だろう?)したというメッセージがはっきりと聞こえます。

 

で、

 

実際音楽を流してみると、

 

(1)録音ボリュームレベルの違いがあるが、イアプラグ型に比べると同じiPhoneの出力レベルだと音量は小さめ

(2)(それがメリットだが)耳の穴(外耳道)が空いているので、外部音がイアプラグ型に比べると大き目で、早朝の住宅街投静寂な場所ならともかく、幹線道路沿いに歩く場合などは音楽はかき消されてしまいます。(これは安物でなくてもそうみたい。)

(3)それでも機構とすると音量(ボリューム)レベルを倍程度に上げる必要がありますので、イアプラグ型に戻す場合、必ずボリュームレベルを戻しておく必要があります。

(4)前後に頭を動かすと後頭部のフレームが服に触って、音量が変化したので、試しに骨伝導イヤホーン/スピーカーを動かしてみると、

 ①(当たり前だが)頭蓋骨に軽く押し付けた方が音量が上がる

 ②(私の場合)蟀谷(コメカミ)より、耳珠(ジジュ-トラガス、外字の小さなとがった隆起)に近い方が音量が上がります。

(5)なお、骨伝導イヤホーン/スピーカーは振動子ということですが、僅かに音を出しております。(音漏れするそうです。)

 

ということが分かりました。

 

まぁ、こんなものか。

 

というのが総括で、安いなりに十分使えますが、使う場所を選んだほうが良いでしょう。早朝ウォーキングは静かなので問題ありませんが、日中は厳しいでしょう。意外と静かな自宅の、自分の部屋でPCをいじりながら、読書しながら使うのが一番合っているかもしれません。

 

ps. 今回の製品はユーザーズマニュアルの各部の名称に「骨伝導イアフォーン/スピーカー」や「フレーム」「耳掛け」などの表示が無く、標準装着方法の指示が無いので合格点は上げられませんね。

このシリーズ物の【無駄話】は、お気楽にOpenGLの簡単なクラス化を夢見てこう始まって、ここまで来ました。より具体的に書くと、

 

(1)Dev-C++のサンプルを解析して、WindowsでサポートするOpenGL 1.1(ヘッダーファイルはBCC102の "...BCC102\include\windows\sdkGL\” にあるgl.hとglu.h)を使った2Dのグラフィックサンプルを使えるように、(Win32 APIのGetDC、ReleaseDCのように)自作GetRC(Rendering Context)とReleaseRC関数、SizeChanged関数とGLLoop(OpenGL用のループ)関数等を作り、2Dグラフィックスの描画は難なく対応できました。(末尾【ご参考】参照)

 

(2)CGやるならやりたいのは3Dグラフィックスでしょうが、これはOpenGL(gl.hとglu.h)だけでは大変なので、既に奇特な方が作成されたglutを使おうと思いました。しかし、オリジナルのglutは旧いし、20世紀末からバージョンアップされていないので、その正統後継と言われるfreeglutを導入することになりました。(その間のすったもんだについてはこれ参照)

 

(3)glut(及びfreeglut)というのは、「クラス化によるオブジェクト指向言語となるC++」以前の「手続志向言語としてのC」で書かれたようなライブラリーであり、実際"int main(int argc, char** argv)"をエントリーポイントとし、その後初期化、表示用ウィンドウ作成、コールバック関数の設定()を行い、表示用ウィンドウのメインループ(メッセージ処理をしない時のglutIdleFunc関数を含め)に入ります。このように、「(free)glutので作るウィンドウを使用することが前提のライブラリーであり、ユーザーウィンドウにグラフィック表示を行うライブラリーではない」所に特徴があり、このお作法から外れた処理の仕方をする場合、相当に腰を据えてかからないとうまく動作しないことを思い知らされました。

:(free)glutではmain関数内で以下のようなシーケンスで表示用ウィンドウを作り、描画準備を行います。

    // 初期化設定
    glutInit(&argc, argv);                           // glutを初期化する
    glutInitWindowSize(800, 600);             // 画面サイズを指定する
    glutInitWindowPosition(100, 100);        // 画面の初期位置を指定する
    glutInitDisplayMode(GLUT_RGBA);        // 表示モード設定
    glutCreateWindow("OpenGL Window"); // ウィンドウの名前
    // コールバック関数の設定
    glutDisplayFunc(display);                     // 描画処理が必要なときに呼ばれる
    glutReshapeFunc(reshape);                  // reshapeが必要なときに呼ばれる
    glutIdleFunc(idle);                               // 処理メッセエージが無いときに呼ばれる
    glutMainLoop();                                  // 毎フレームのLoop

 

(4)具体的には、OpenGLで使える関数のうち"gl~"関数(gl.h)、"glu~"関数(glu.h)は(換言すれば2D処理は)問題なく使えるのですが、"glut~"関数(glut.hまたはfreeglut_std.h-)は失敗(プログラムの異常終了-「落ちる」)します。これに対して、try~catchを使ってglGetError()関数で問題特定しようとしたりしましたが、結局原因は特定できませんでした。(OpenGL 4.0から利用できるようになったエラーメッセージコールバック関数<void GLAPIENTRY MessageCallback>は未定義で使えませんでした。)

:特に以下の組み込み3DCG描画関数

  glutSolidTeapot(GLdouble size);   //glutWireTeapot(GLdouble size); 以下相対のワイアーフレーム処理も同じ
  glutSolidCube(GLdouble size);
  glutSolidSphere(GLdouble radius,GLint slices,GLint stacks);
  glutSolidTorus(GLdouble innerradius, GLdouble outerradius, GLint nsides, GLint rings);
  glutSolidOctahedron(void);
  glutSolidTetrahedron(void);
  glutSolidDodecahedron(void);
  glutSolidCone(GLdouble radius, GLdouble height, GLint slices, GLint stacks);

 

(5)この為、唯一ユーザーウィンドウと末尾の関数で処理できる3D CGは、ユーザーが定義する図形を、

        glBegin(GLenum mode);
        glVertex3dv(GLdouble x, GLdouble y, GLdouble z);

        glEnd();

等を使って描画することに限定されます。

まぁ、3D CGドシロートの私ではここまでが限界かもしれません。「3D CGが好きなので」という方はこんな面倒なことをしないで、freeglutをそのまま使って、もっと簡単に楽しめますよ。

根性があれば、「クソッ、このティーポットの頂点情報を入力してやるっ!」となるのですが、何せヘタレの老人なので、そんなパワーは全くありません。実際、ここまで来るだけでヘトヘトで、そんなにCGが好きなわけでもないので、テーマを変えたくなって仕方がありませんでした。

 

そろそろ、ここいら辺でOpenGLから開放してもらおうかな?

 

【ご参考-C++でWindows SKDベースで書いています

/************************
 OpenGL Includesファイル
 ************************/

#include    <GL\gl.h>                //OpenGLをWindowsで使う場合必須
#include    <GL\glu.h>                //OpenGLユーティリティ ライブラリをWindowsで使う場合必要
/*
glutを入手し、本プログラムでコンパイルしたら、glut.hとstdlib.hのexit関数が二重定義になっているというエラーが出た。
解決法はstdlib.h(windows.hに入っている)を先に取り込め、とあったが解決にはならなかった。
結局、glutのサイトからフルバージョンのファイルをダウンロードし、その中のglut.h(エラーが起こったものとは異なる内容)を使って解決した。
*/

    //クラス化も考えて"m_"にしていますが、外部変数です。
    bool m_GLon = FALSE;            //OpenGLを使用中か否か
    HWND m_hWnd;                    //OpenGLでグラフィックを表示するウィンドウのハンドル
    HDC m_hDC;                        //対象ウィンドウのデバイスコンテキストハンドル
    HGLRC m_hRC;                    //OpenGL用のレン. ダリング・コンテキスト
    PIXELFORMATDESCRIPTOR m_pdf;    //ピクセルフォーマット構造体
    int m_pixelformat;                //ChoosePixelFormat関数の戻り値:指定されたピクセル形式記述子に最も近いピクセル形式インデックス
    int m_DrawMenuNo = IDM_2D01;    //描画関数を選択する描画番号(初期値は2DのSample01)

#include    "DrawFunctions.h"        //OpenGLによる描画関数を記述したファイル

    ///////////////////////////////////////
    //GetRC-レンダリングコンテキストの取得
    //WM_CREATE時に実行する
    ///////////////////////////////////////

    HGLRC GetRC(HWND hWnd) {        //これはコンストラクターに入れる内容かも

        static PIXELFORMATDESCRIPTOR pfd = {
                sizeof(PIXELFORMATDESCRIPTOR),    //この構造体のサイズ
                1,                                //OpenGLバージョン(Windowsが利用できるのは1.1のみ)
                PFD_DRAW_TO_WINDOW |            //ウィンドウ描画
                PFD_SUPPORT_OPENGL |            //OpenGL使用
                PFD_DOUBLEBUFFER,                //ダブルバッファ使用
                PFD_TYPE_RGBA,                    //RGBAを使用
                24,                                //24bit色を使用
                0, 0, 0, 0, 0, 0,                //カラー(RGB)ビット無視
                0, 0,                            //アルファ(A)バッファ無し
                0,                                //アキュムレーションバッファ不使用
                0, 0, 0, 0,                        //アキュムレーションbit無視
                32,                                //Zバッファー
                0,                                //ステンシルバッファ無し
                0,                                //補助バッファ不使用
                PFD_MAIN_PLANE,                    //メインレイヤー(レイヤー種類: PFD_MAIN_PLANE、 PFD_OVERLAY_PLANE、 PFD_UNDERLAY_PLANE)
                0,                                //予約
                0, 0, 0                            //レイヤーマスク不使用
        };
        //出力ウィンドウのデバイスコンテキストを取得
        m_hDC = GetDC(m_hWnd = hWnd);            //対象ウィンドウのハンドルを記録し、デバイスコンテキストを取得
        if(!m_hDC) {
            MessageBox(m_hWnd, "ウィンドウのデバイスコンテキストハンドルの取得に失敗しました。", "エラー", MB_OK | MB_ICONERROR);
            return NULL;
        }
        //同ウィンドウの最適ピクセルフォーマットインデックスを取得
        if((!(m_pixelformat = ChoosePixelFormat(m_hDC, &pfd)))) {    //0が返った場合
            MessageBox(m_hWnd, "ChoosePixelFormat関数が失敗しました。", "エラー", MB_OK | MB_ICONERROR);
            return NULL;
        }
        //最適ピクセルフォーマットの設定
        if(!SetPixelFormat(m_hDC, m_pixelformat, &pfd)) {            //FALSEの場合
            MessageBox(m_hWnd, "SetPixelFormat関数が失敗しました。", "エラー", MB_OK | MB_ICONERROR);
            return NULL;
        }
        //OpenGLレンダリングコンテキストの作成
        if(!(m_hRC = wglCreateContext(m_hDC))) {
            MessageBox(m_hWnd, "OpenGLレンダリングコンテキスト(HGLRC)の生成に失敗しました。", "エラー", MB_OK | MB_ICONERROR);
            return NULL;
        }
        //m_hRCをレンダリングコンテキストに指定し、対象ウィンドウのDCに描画(最後にhRCをNULLにして再度wglMakeCurrentを呼び出し、指定を解除することが必要)
        wglMakeCurrent(m_hDC, m_hRC);
        //OpenGLを使用中にする
        m_GLon = TRUE;
        return m_hRC;
    }

    ///////////////////////////////////////
    //OpenGLを使用中か否かのフラグチェック
    ///////////////////////////////////////

    bool IsGL() {
        return m_GLon;
    }

    ////////////////////////////////////////////
    //SizeChanged-WM_SIZE時のビューポートの取得
    ////////////////////////////////////////////

    void SizeChanged(LPARAM lParam) {

        glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));    //ウインドウ全体に表示
        glMatrixMode(GL_PROJECTION);    //投影変換モードへ
        glLoadIdentity();                //投影変換の変換行列を単位行列で初期化
        glOrtho(-1.0, 1.0, -1.0, 1.0, 1.0, -1.0);    //各軸-1.0~1.0で囲まれる立方体の範囲を並行投影
        glMatrixMode(GL_MODELVIEW);        //視野変換・モデリング変換モードへ
        glLoadIdentity();                //視野変換・モデリング変換の変換行列を単位行列で初期化
    }

    //////////////////////////////
    //対象ウィンドウGL描画用ループ
    //////////////////////////////

    void GLLoop(MSG msg) {

        //アイドル時に描画させるメッセージループ
        while(msg.message != WM_QUIT) {
            if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
                //WM_QUITを明示的に処理する
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
            //処理メッセージが無ければ描画する
            else {
                switch(m_DrawMenuNo) {    //描画関数を選択する変数
                case IDM_2D01:
                    Draw2D01();        //2D-Sample01
                    break;
                case IDM_2D02:
                    Draw2D02();        //2D-Sample02
                    break;
                case IDM_2D03:
                    Draw2D03();        //2D-Sample03
                    break;
                case IDM_2D04:
                    Draw2D04();        //2D-Sample04
                    break;
                case IDM_2D05:
                    Draw2D05();        //2D-Sample05
                    break;
                case IDM_3D01:
                    Draw3D01();        //3D-Sample01
                    break;
                case IDM_3D02:
                    Draw3D02();        //3D-Sample02
                    break;
                case IDM_3D03:
                    Draw3D03();        //3D-Sample03
                    break;
                case IDM_3D04:
                    Draw3D04();        //3D-Sample04
                    break;
                case IDM_3D05:
                    Draw3D05();        //3D-Sample05
                    break;
                default:
                    break;
                }
            }
        }
    }

    ///////////////////////////////////////////
    //ReleaseRC-レンダリングコンテキストの解放
    //WM_DESTROY時に実行する
    ///////////////////////////////////////////

    void ReleaseRC() {    //これはデストラクターに入れる内容

        //カレントレンダリングコンテキストを解放
        wglMakeCurrent(NULL, NULL);
        //レンダリングコンテキストの解放
        wglDeleteContext(m_hRC);
        //対象ウィンドウのデバイスコンテキストを開放
        ReleaseDC(m_hWnd, m_hDC);
        //OpenGLの使用を終了
        m_GLon = FALSE;
    }
 

    ///////////////////
    //描画メニュー処理
    ///////////////////

    bool DrawMenu(int no) {    //引数は新しい描画メニュー番号

        ReleaseRC();    //一旦レンダリングコンテキストを開放する。
        CheckMenuItem(GetSubMenu(GetMenu(m_hWnd), 1), m_DrawMenuNo, MF_BYCOMMAND | MF_UNCHECKED);
        m_DrawMenuNo = no;
        CheckMenuItem(GetSubMenu(GetMenu(m_hWnd), 1), m_DrawMenuNo, MF_BYCOMMAND | MF_CHECKED);
        GetRC(m_hWnd);    //新たにレンダリングコンテキストを取得する。
        return TRUE;
    }
 

前回までの「Python初体験」の続きです。

プログラムによる処理の流れには次の3つ
(1)処理を上から順番に一つずつ実行する    -順次処理
(2)条件によって処理を選択する                -分岐処理
(3)条件が満たされている間、処理を繰り返す-反復処理
があるというところから始まります。この内、(2)と(3)は条件式で制御することはどのようなプログラミング言語でも同じですね。

前にPythonの比較演算子について触れました。("is"はオブジェクトの同一性チェックなので以下では触れません。)
"""\
(4)比較演算子
x == y       x と y が等しい        #C++等と同じですね。
x != y       x と y が等しくない    #C++等と同じですね。
x > y        x は y よりも大きい    #これ以降はどの言語でも同じですね。
x < y        x は y よりも小さい
x >= y       x は y と等しいか大きい
x <= y       x は y と等しいか小さい
x in y       x という要素 が y に存在する    #Python独特
x not in y   x という要素 が y に存在しない  #Python独特

なお、予約語'False'('false'だとエラーになる)は0と等価です。(因みにprint(1 == True)も試しましたが、Trueでした。)
print(0.0 == False)  #Trueになる\
"""

このTrue(1)とFalse(0)というbool型の結果で条件式を作るのも他の言語と変わりません。なお、講義は文字列の比較について1ページ設けており、
(1)数字は、文字の0が最小、9が最大(0 < 1 < 2 < ... < 9)
(2)アルファベットでは、a が最小、z が最大 (a < b < c < ... < z)
(3)大文字は小文字より小さい (A < a, B < b, ...)
(4)数字は、アルファベットよりも小さい (0 < 9 < A)
という文字列比較の原則を説明しますが、これはASCIIコード表Unicode番号を文字に当てはめればお分かりかと存じます。(従って他の言語と変わりはありません。)

次に出てくるのが、(当然のことですが)if文です。ifを使った条件分岐は言語によってお作法が違います。例えば私が経験したプログラミング言語では、
(1)BASIC
   if 条件文 then 処理1 else 処理2  '言語によりIF、If、THEN、Then、ELSE、Else(及びブロック終結のEnd If)となるものがあります。
(2)C、C++、C#
   if(条件文) {  //処理1、2が単文であれば{}は省略可
        処理1
      }
      else {       //'{'は改行して書いてもよい。C#は改行する人が多いようです。
        処理2
      }
ですが、Pythonの場合は、
(3)Python
   if 条件文 :  #if、elseの後、条件式は()不要で最後に':'が必須。また処理1、2の文は「必ずスペース4文字で字下げしなければならない」(注)
        処理1 
      else:        #字下げされた部分がブロックになる。(
        処理2
の様に書くようです。(条件式にはbool値を返すメソッドを使ってもよいことは他の言語と変わりません。)
:これをサッカーに因んで「オフサイドルール」というようです。「複数行を継続したときにインデントする場合は、4つスペースを使うルールを守らなくても構いません。」ともいわれますが、いずれにせよその他にも結構厳しい「お作法」が定められているようですね。(私は「タブ」派だったので「スペース4つ」はちょっと違和感があります。)

複数条件式の場合は、if~elseをネストするのか、と思いましたが、専用のelifキーワード(Python特有)があるようです。
if 条件式1:
    処理1
    ...
elif 条件式2:
    処理2
    ...
elif 条件式3:
    処理3
    ...
else:
    処理n
    ...

大分進んだので今日は此処までですね。
 

前回までの「Python初体験」の続きです。

今回はデータ(オブジェクト)を処理するメソッド(関数)についてでした。Pythonにとどまらず、C++やC#などのOOP(Object Oriented Programing)系言語なら、

クラス=設計された型
オブジェクト=型に基づく実体(メモリーに置かれる)
メンバー=クラスが持つ変数(フィールド)、関数(メソッド-戻り値がvoidの場合を含む-「オブジェクト指向プログラミング言語において、あるクラスまたはオブジェクトに所属するサブルーチンを指す。」by wiki)等
プロパティ=メンバー変数とそれに関わるアクセサー(ゲッター<C#等のget>、セッター<C#等のset>)で、「フィールドの操作と同じ構文でアクセッサーを定義するための言語機能および構文の一種である。」by wiki


のような概念に慣れていると思います。Colabの紹介では「そのデータ(解説:オブジェクト)を操作するための、専用の関数(解説:メンバー関数ですね)」として、

text = "lower letters" # 変数 text に 文字列 "lower letters"を代入
uppered_text = text.upper() # upper() メソッドで、大文字を作成
print(uppered_text)
(出力)LOWER LETTERS

text = "The shells she sells are sea-shells, I'm sure."
text.find("sea")
(出力)25

data = 0.5
data.as_integer_ratio()
(出力)(1, 2)

等が載っていました。(赤太字メソッド)まぁ、(C++やC#をやっている人なら)ここら辺は特に驚くようなことは無いですが、ね。

ps. なお余談ですが、「Pythonでは変数の型を宣言する必要はありません。動的型付けによって、型が自動的に判別されます。」ということです。後でまたやるかもしれませんが、Pythonの変数型は、
"""\
整数 (int型): 数値を表すデータ型です。例えば、3は整数型の値です。
小数 (float型): 小数点を含む数値を表すデータ型です。例えば、1.23はfloat型の値です。
文字列 (str型): 文字列を表すデータ型です。例えば、"エクスカリバー"や'エクスカリバー'はstr型の値です。
真偽値 (bool型): TrueまたはFalseの値を持つデータ型です。
リスト (list型): 複数の要素を順序付けて格納するデータ型です。例えば、[1, 2, 3]はlist型の値です。
タプル (tuple型): リストと似ていますが、変更できない要素の集合を表すデータ型です。例えば、(1, 2, 3)はtuple型の値です。
辞書 (dict型): キーと値のペアを持つデータ型です。例えば、{"one": 1, "two": 2, "three": 3}はdict型の値です。\
"""

となっているそうです。「実数」でなく「小数」としたり、C++なら"const list"っぽい「タプル」(注)など、ちょっと変わっていますね。では、また。

注:英語のtupleとは「複数の構成要素からなる組を総称する一般概念」という数学用語のようです。

 

に、既にobsolescence(陳腐化)の域に入り込んでいるともいえるOpenGLに突如関心をもった話を書きました。その理由は、単に「ブログを書くために、C++のIDEの調査の為ダウンロードしたDev-C++のサンプルにあったWin32 SDKのプログラム」

(↑haOpenGLのサンプルを基に自作した"GL Sampler")

がOpenGL(但し、そのサンプルは2Dグラフィック)だったから、なんですが。

 

で、

やろうかとおもってwebをググってゆくと、この世界はCGの世界につながっていて

気が遠くなるように広いぃぃぃぃ...

 

これは「脳みそが爆発するか、この世に帰ってこれなくなるぞ」と思い、2D5つ、3D5つくらいのサンプルを表示する"GL Sampler"を作って、手をパンパン、「おつかれっしたぁー」にしようと思ったのですが...

 

(1)先ず、私のC++開発環境は旧Borlandの承継会社Embarcaderoがフリーで配布しているC++コンパイラー("BCC102"のbcc32c.exe)であり、bcc32c.exeでもコンパイルできた問題のサンプルを眺め、webをググって、WindowsのOpenGLがVersion 1.1で、gl.hとglu.hをインクルードするだけで使えることが分かりました。(因みに↑の2Dのデモはこのレベルで対応できます。)

 

(2)ただし、↑のサンプルなどWin32APIのGDIでできちゃうので、「OpenGLをしゃぶる」には3DのCGをしなくてはなりませぬ!しかし、それには結構ハードルが高いし、関連サイトを見ているとヘッダーファイルにない"glut.h"をインクルードして何やら楽しそうなことをやっています。

ん、んんん、んんんんんん、GLUTって何?

というところでドツボへの第一歩を踏み出しました。

 

(3)glutというのはOpenGL Ulitity Toolkitの略だそうで、既に先人が「なんと、オープンで!!!」OpenGL用のライブラリーを作ってくれていたようです。

そんじゃ、遠慮なく、ごっそさんでーす!

となるはずだったのですが...

 

(4)Webでは何やらglutはVersion 3.7で1998年から更新されていない、バグっぽい所もある等の話がwebにあり、(後光の指しているボランティアの方がしこしこと継承して)その後継のfreeglutがあるらしいことが分かりました。しかし読んでみると、これはソースレベルで提供されており、DLLやlibはCMakeというMakeユーティリティでコンパイルしなければならない、Embarcadero C++にはVisual Studioベースにコンパイルしてから改造しなければならない、等の話で、

あーヤメヤメ止めっ!

となりました。

 

(5)でも、だれか、なんかあるだろう?というセコさで調査を継続し、とうとう、GLUT for Win32のWeb ページから最新のWindows VC++用precompiledファイル glut-3.7.6-bin.zip がダウンロードできるというところまで調べました。(注)
注:GLUTfreeGLUTを利用するには、フルサイズの圧縮ファイルをダウンロードしてコンパイルする必要があり、ちょっと遊ぶだけの私の場合は、必要なファイルだけダウンロードすればいいや、と思っていました。

 

(6)ダウンロードしたファイルのうち、GLUTのライブラリー(glut32.lib)は、Microsoft Visual C++用(COFF形式)なので、次のようにBCC用のライブラリ(OMF形式)を作成しなければなりません。これはBCC102のbinフォールダーにあるimplib.exeを使えばよいのです。(PoweShellは同じフォールダーのコマンドでも".\"が必要)
.\implib glut32.lib glut32.dll(Enter)
後はOMF用のライブラリー元のライブラリーファイルと入れ替えます。

そして最後に以下の3ファイルを所定のフォールダーにコピーします。(以下は私の場合)
(1)glut32.dllをC:\Windows\SysWOW64へ(System32ではないので注意!
(2)glut.hをC:\Borland\BCC102\Inlcude\gl
(3)glut32.libをC:\Borland\BCC102\lib\win32c\release\psdk
もちろんbcc32c.exeの初期設定ファイル(bcc32c.cfg)に
-L@\..\lib\win32c\release\psdk
というパスが通っているか確認します。

 

(7)最初、"exit"関数のglut.hとstdlib.hによる二重定義エラーがでて、最初の往生がありましたが、それは新しいglut.hをご本家のダウンロードファイルからとってきて交換することで何とか乗り越えられ、その後は(当たり前ですが)glutのサンプルプログラムが3Dも含めて一応使えるようになりました。

しかし冒頭の自作"GL Sampler"で3Dサンプルを表示させようとすると何も表示しません。これは色々とチェックし、色々と調べCopilotやChatGPTにも縋って点検しましたが、何が問題であるか分かりませんでした。

で、これはglut(glut32.dll)が旧い、旧すぎるんじゃないか?

と疑ったわけです。

 

(8)ここまで来ると病膏肓。

新しいfreegluを導入しよう、絶対!

という妄信となり、さらに調査してfreeglut Windows Development LibrariesというところのDownload freeglut 3.0.0 for MSVCdでVC++用のプリコンパイル版をゲット!

↑のと同じように、

(1)freeglut.dllをC:\Windows\SysWOW64へ(同じファイルをgult32.dllとしても登録)
(2)glut.hとfreeglut*.hすべてをC:\Borland\BCC102\Inlcude\glへ
(3)freeglut.dllからimplib.exeで作ったfreeglut.libとglut32.libをC:\Borland\BCC102\lib\win32c\release\psdkへ
ファイルを配置し、いざコンパイル!

所が、ところが、

今度はリンカーエラーが大量に発生します。glutのサンプルプログラムだけでも、

Error: Unresolved external '__glutInitWithExit' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\TESTGLUT001-291191.O
Error: Unresolved external '__glutCreateWindowWithExit' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\TESTGLUT001-291191.O
Error: Unresolved external 'glutDisplayFunc' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\TESTGLUT001-291191.O
Error: Unresolved external 'glutMainLoop' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\TESTGLUT001-291191.O
Error: Unresolved external 'glutInitWindowPosition' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\TESTGLUT002-034016.O
Error: Unresolved external 'glutInitWindowSize' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\TESTGLUT002-034016.O
Error: Unresolved external 'glutInitDisplayMode' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\TESTGLUT002-034016.O
Error: Unresolved external 'glutReshapeFunc' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\TESTGLUT003-379084.O
Error: Unresolved external 'glutPostRedisplay' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\TESTGLUT005-452307.O
Error: Unresolved external 'glutKeyboardFunc' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\TESTGLUT005-452307.O

こんなに発生し、基本的なglutMainLoop等の関数まで参照できずにいます。

この為、何度もlibファイルをチェックし、登録されていることを確認し、libファイルの置き場所を変えたりしましたが、依然エラーは解消できず、

結局
ソースコードにlibファイル指定の#pragma comment(lib, "freeglut")をいれて参照を強制せざるを得ませんでした。

でも、glut32.libは参照するのに、freeglut.libは参照しない、

なんでなんだろう?

分からん。

 

(9)ここまで来ると、もう意地というかなんというか、

絶対に解決してやるという根性

だけです。この#progma文のおかげで、エラーが出ていたサンプルプログラムがコンパイルできるようになり、とうとうGLUTのサンプルプログラムで(実はこれがやりたかったのですが)

WireTeapotを表示することが出来ました。

そして、

いよいよ自作の"GL Sampler"でTeapotを表示しようとしたら、

ストン、と落ちてしまう(プログラムが異常終了すること)

じゃないですか!

何故なんだーっ!

と空しく叫ぶだけの老人(今年古希)でした。

 

ps. 絶対にりべんじしてやる!

 

前回までの「Python初体験」の続きです。

今回は文字列の解説です。

まず、大した話ではないのですが、Pythonでは""''がおんなじで、CやC++(C#も)が''で囲む「文字」と""で囲む「文字列」を峻別するので、それに慣れた私はちょっと吃驚しました。さらにまた、トリプルクオート("""または'''により、改行を含む複数行文字列を表すことが出来るそうです。
例えば、'ABC'"ABC"、さらに''ABC''""ABC""皆同じ文字列です。また、
'''ABC
DEF'''


"""ABC
DEF"""

は、"ABC\nDEF"と同じだそうです。

 

C系言語(をやってきた私にとっての最も注意すべきことですが)との大きな違いは、Pythonにはヌル文字がなく)、ヌル文字は単なる'\0'またはb'\0'の意味しかありません。
:「ヌル(null)文字

この理由は、そもそもPythonでは文字列が文字列長情報を持つバイナリーデータなので、ヌル文字による終端記号(Terminator-文字列の終わりを知らせるサイン)の必要がない訳です。()なお、文字列=""は空文、即ち長さ0の文字列データが「ある」-not None)です。
:バイナリーデータはエンコードの種類で異なります。Pythonの文字列デフォルトはUTF-8ですが、文字列.encode('shift_jis')とか文字列.encode(encoding='utf_16_le')で文字列データが変わります。なお、Copilot君によれば「Pythonの文字列は、内部的にはUnicode文字列として表現されます。Unicodeは、世界中のすべての文字を表現するための標準的な文字コードです。Pythonの文字列は、UTF-8エンコーディングでエンコードされたUnicode文字列として表現されます。UTF-8は、Unicode文字をバイト列に変換するための一般的なエンコーディング形式の1つです。」とのことです。またC系言語で終端記号は"NULL"や"null"などと書かれ、ASCIIでは1byteの\0、WindowsのUTF-16では2bytesの\0\0となります。

では、C系言語で変数の実体がないもの(例:文字列ポインターchar* ptr = null(0);→ポインター変数ptrはメモリーに存在するが、それが差し示す文字列はメモリーに無い。)のような「不存在(無い)」はどのように扱われるのでしょうか?PythonではそれをNoneといって、「不存在」を表すようです。

例えば、

fruits = {1:"apple", 2:"lemon", 3:"melon"}
print("fruisは3までしかありません。fruits.get[4]は", fruits.get(4), "となります。")

(出力)fruisは3までしかありません。fruits.get[4]は None となります。

dict = {"リンゴ":1, "レモン":2, "メロン":3}
print("辞書(dict)に「西瓜」は", dict.get("西瓜"), "です。")

(出力)辞書(dict)に「西瓜」は None です。

のように書くのだそうです。吃驚!

なお、条件式のis==の違いは「is演算子がオブジェクトの同一性を判定するのに対し、==演算子はオブジェクトの値の等価性を判定する」ということは、前回学習しましたね。
 

では、また次回。