coLinux日記

coLinuxはフリーソフトを種として、よろずのシステムとぞなれりける。

Raspberry Pi に Python 3.13.1 をインストールする。

2024-12-05 18:46:25 | Python
Python 3.13.1 がリリースされました。早速アップデートします。これで、Python 3.13 もメンテナンスリリースモードになりました。定期的にアップデートされていくと思います。

https://www.python.org/downloads/release/python-3131/ の冒頭は、以下のようです。

Python 3.13 は Python プログラミング言語の最新メジャー リリースであり、Python 3.12 と比較して多くの新機能と最適化が含まれています。 3.13.1 は最新のメンテナンス リリースで、3.13.0 以降の約 400 のバグ修正、ビルドの改善、ドキュメントの変更が含まれています。

早速ソースをダウンロードしましょう。

$ wget https://www.python.org/ftp/python/3.13.1/Python-3.13.1.tar.xz
$

$ md5sum Python-3.13.1.tar.xz;echo 80c16badb94ffe235280d4d9a099b8bc
80c16badb94ffe235280d4d9a099b8bc Python-3.13.1.tar.xz
80c16badb94ffe235280d4d9a099b8bc
$

$ wget https://www.python.org/ftp/python/3.13.1/Python-3.13.1.tar.xz.asc

$ gpg --verify Python-3.13.1.tar.xz.asc
gpg: assuming signed data in 'Python-3.13.1.tar.xz'
gpg: Signature made Tue 03 Dec 2024 19:06:34 GMT
gpg: using RSA key 7169605F62C751356D054A26A821E680E5FA6305
gpg: Good signature from "Thomas Wouters <thomas@python.org>" [unknown]
gpg: aka "Thomas Wouters <thomas@xs4all.nl>" [unknown]
gpg: aka "Thomas Wouters <twouters@google.com>" [unknown]
gpg: aka "Thomas Wouters <thomas.wouters.prive@gmail.com>" [unknown]
gpg: aka "Thomas Wouters <thomaswout@gmail.com>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 7169 605F 62C7 5135 6D05 4A26 A821 E680 E5FA 6305
$


12月3日にサインされています。3.13.0 の時と同じように make します。今回は time コマンドで時間を計ってみました。

$ tar xvf Python-3.13.1.tar.xz
$ cd Python-3.13.1/
$ ./configure --prefix=/usr/local/python --with-readline --enable-shared --enable-optimizations
..............................
configure: creating ./config.status
config.status: creating Makefile.pre
config.status: creating Misc/python.pc
config.status: creating Misc/python-embed.pc
config.status: creating Misc/python-config.sh
config.status: creating Modules/Setup.bootstrap
config.status: creating Modules/Setup.stdlib
config.status: creating Modules/ld_so_aix
config.status: creating pyconfig.h
configure: creating Modules/Setup.local
configure: creating Makefile
$
$ time make
........The necessary bits to build these optional modules were not found:
_dbm
To find the necessary bits, look in configure.ac and config.log.

Checked 112 modules (33 built-in, 77 shared, 1 n/a on linux-aarch64, 0 disabled, 1 missing, 0 failed on import)
make[1]: Leaving directory '/home/espiya/src/Python-3.13.1'

real 51m52.210s
user 49m17.303s
sys 1m36.002s
$


やはり、このRaspberry Pi では、52分くらいかかりますね。
一応、テストも行っておきます。

$ make test
........
21 tests skipped:
test.test_asyncio.test_windows_events
test.test_asyncio.test_windows_utils test.test_gdb.test_backtrace
test.test_gdb.test_cfunction test.test_gdb.test_cfunction_full
test.test_gdb.test_misc test.test_gdb.test_pretty_print
test_android test_dbm_ndbm test_devpoll test_free_threading
test_ioctl test_kqueue test_launcher test_msvcrt test_startfile

4 tests skipped (resource denied): test_winapi test_winconsoleio test_winreg test_winsound test_wmi

test_peg_generator test_tkinter test_ttk test_zipfile64

2 re-run tests:
test_trace test_zipfile

2 tests failed:
test_trace test_zipfile

451 tests OK.

Total duration: 13 min 28 sec
Total tests: run=44,581 failures=2 skipped=1,991
Total test files: run=476/478 failed=2 skipped=21 resource_denied=4 rerun=2
Result: FAILURE then FAILURE
make: *** [Makefile:2194: test] Error 2
$


test_zipfile の部分がエラーになっているところみたいです。
ほとんど問題なさそうなので、そのままインストールします。

$ make install

$
$ python3 --version
Python 3.13.1
$


通常の使い方では、これでよさそうです。
今までの経緯から推測して、おそらく2か月に一度くらいのアップデートがあって、現在テスト中の 3.14 を待つことになるようです。今後は 3.13.1 を使用します。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

入門者が、Python プログラミングにおいて、生成AI(Copilot)を使ってコメントとdocstring を付ける。

2024-10-30 18:11:29 | Python
以前紹介した、
PyFlo The beginners guide to becoming a Python programmer
https://pyflo.net/
の中で、「Guided Project 1」をクリックすると、
Flag Creater
https://pyflo.net/flag/
というものがありました。

文化の日も近いので、日の丸を文字で作るプログラムを作ってみました。
Wikipedia で日章旗を調べると、
縦を2とすると横が3、その中央にある円の直径が縦の 3/5 となっています。
縦を 2.0 とすると、横は 3.0 となり、 中央の円の半径は 2 * (3/5) / 2 = 0.6 となります。
半角文字の大きさの比率がだいたい縦:横 2:1 となるので、1行の文字数は旗の左下隅を X-Y平面の原点においたとして、
その座標は、
左上 (0,20) 、 右上 (60,20) 、 
左下 (0,0) 、 右下 (60,0)
としました。変換してそれぞれ、(0.0 , 2.0) 、(3.0 , 2.0)、 (0.0 , 0.0)、(3.0 , 0.0)になります。

これでプログラムを作ってみます。関数も使わない単純なものですが、このプログラムにコメントや docstring を入れるために、 Microsoft Copilot を使いました。

まず、docstring 生成を次のプロンプトで依頼しました。そのとき、プログラムがどのようなものかを簡単に説明する文を陽に入れてみました。

「次のPython プログラムの docstring を生成して下さい。
これは、日章旗を文字で生成するためのプログラムです。
日章旗は、縦の長さを2、幅の長さを3、中央の日の丸の半径を 0.6 になるように作成します。

w = 60
h = 20
a0 = '+' + '-'*(w-1) + '+'
a20 = '+' + '-'*(w-1) + '+'
a = '|' + ' '*(w-1) + '|'

print(a0)
for i in range(1,h):
   y = (h - i) / (h / 2)

   b = a[0]
   for j in range(1,w):
      x = j / (w / 3)

      if ( (x-1.5)**2 + (y-1.0)**2 < 0.36 ) :
         b += '*'
      else:
         b += a[j]
   b += a[-1]

   print(b)

print(a20) 」


Copilot の回答は以下の通りでした。

ここから======
もちろん、こちらがあなたのプログラムのためのdocstringです:

def draw_nisshoki():
   """
   This function prints the Japanese national flag (Nisshoki) using ASCII characters.

   The flag's proportions are a height to width ratio of 2:3, with a red circle (the sun)
   in the center. The circle's radius is 0.6, scaled appropriately to fit within the grid.

   The border of the grid is made up of '+' and '-' characters for the top and bottom,
   and '|' characters for the sides. The sun is drawn using '*' characters inside the grid.

   Variables:
   w (int): The width of the grid.
   h (int): The height of the grid.
   a0 (str): The top border of the grid.
   a20 (str): The bottom border of the grid.
   a (str): The side borders of the grid.

   The function uses nested loops to calculate the positions of the '*' characters
   that form the sun based on the coordinates (x, y) within the grid.
   The equation `( (x-1.5)**2 + (y-1.0)**2 < 0.36 )` is used to determine whether
   a particular point should be part of the sun shape or not.
   """
   w = 60
      .................
      (プロンプトで入力したプログラムなので途中省略)
      .................
   print(a20)

======ここまで


関数にされてしまいました。しかもなぜか英語でした。これは、docstring は、関数やクラスの中で使うかららしいです。 一応 DeepL で翻訳(少し修正あり)しておきます。

この関数は、日本の国旗(日章旗)をASCII文字で印刷します。

 国旗の縦横比は2:3で、中央に赤い円(太陽)がある。円の半径は0.6で、グリッドに収まるように適切に拡大縮小されています。

 グリッドの境界線は、上下に'+'と'-'、左右に'|'で構成されている。太陽はグリッドの内側に'*'文字を使って描かれます。

 変数

 w (int): グリッドの幅。
 h (int): グリッドの高さ。
 a0 (str): グリッドの上辺。
 a20 (str): グリッドの下端。
 a (str): グリッドのサイドボーダー。

 この関数は,ネストされたループを使用して,グリッド内の座標 (x, y) に基づいて太陽を形成する '*' 文字の位置を計算します.

 方程式 `( (x-1.5)**2 + (y-1.0)**2 < 0.36 )` は、特定の点が太陽の形に含まれるかどうかを決定するために使用されます。

プログラムの内容を、ほぼ正確に表してくれていますね。

そこで、今度はこのプロンプトの docstring を コメント にして再び Copilot にたずねました。

「次のPython プログラムを説明するための分かりやすいコメントをプログラムに追加して下さい。
これは、日章旗を文字で生成するためのプログラムです。
日章旗は、縦の長さを2、幅の長さを3、中央の日の丸の半径を 0.6 になるように作成します。
(この後のプログラムは同じなので省略)」


Copilot の回答は以下の通りでした。

ここから======
以下に、プログラムにコメントを追加しました。これで、各部分の動作がわかりやすくなると思います。

```python
# 幅と高さを設定
w = 60
h = 20

# 上部と下部の枠線を作成
a0 = '+' + '-'*(w-1) + '+'
a20 = '+' + '-'*(w-1) + '+'
# 空白の行を作成
a = '|' + ' '*(w-1) + '|'

# 上部の枠線を表示
print(a0)

# 高さ分のループを開始
for i in range(1, h):
   # y座標を計算(中心を基準にした相対位置)
   y = (h - i) / (h / 2)

   # 行の開始文字を設定
   b = a[0]
   # 幅分のループを開始
   for j in range(1, w):
      # x座標を計算(中心を基準にした相対位置)
      x = j / (w / 3)

      # 中心からの距離が半径0.6以内なら '*' を追加、それ以外は空白を追加
      if ( (x-1.5)**2 + (y-1.0)**2 < 0.36 ):
         b += '*'
      else:
         b += a[j]
   # 行の終了文字を追加
   b += a[-1]

   # 行を表示
   print(b)

# 下部の枠線を表示
print(a20)
```

このコメントが、プログラムの理解に役立つことを願っています!他に質問があれば教えてくださいね。

======ここまで


一言も伝えていないのに、プログラムだけから、

上枠(a0)、下枠(a20)と解釈したり、
「中心を基準にした相対位置」と解釈したり、
if 文で陽に 0.36 としか書いていないのにプロンプトから?「半径0.6」と解釈したりと、

しっかりとコメントしてくれています。これは良いですね。

以上、簡単なプログラムを作成して Copilot でコメントやdocstring を付けてもらいましたが、満足できるものでした。

ということは、Python 入門者は生成AIを活用して自分がプログラミングしたプログラムに対してコメントや docstring を付けてもらい、それを参考にしてさらに知識を得ることが、かなり有用であると言えそうです。

最後にプログラムを動作させたときの画像を表示しておきます。作成したプログラムは flag.py というファイルです。


docstring は、Python の有用な機能なのは、SimplePrograms で学ぶうちに理解しました。そうであるなら、初心者は生成AIを積極的に活用して docstring を使うという流れになるかもしれませんね。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Raspberry Pi に Python 3.13.0 をインストールする

2024-10-13 07:54:13 | Python
PEP719 で告知されたとおり、Python 3.13.0 が 2024-10-07 にリリースされました。

当ブログの以前の記事の通りの方法で、早速 Raspberry Pi にインストールしてみます。

まずは、ダウンロードして、チェックします。(例によって空白文字は適当です。)

$ wget http://www.python.org/ftp/python/3.13.0/Python-3.13.0.tar.xz

$ md5sum Python-3.13.0.tar.xz;echo 726e5b829fcf352326874c1ae599abaa
726e5b829fcf352326874c1ae599abaa Python-3.13.0.tar.xz
726e5b829fcf352326874c1ae599abaa
$

$ wget http://www.python.org/ftp/python/3.13.0/Python-3.13.0.tar.xz.asc

$ gpg --verify Python-3.13.0.tar.xz.asc
gpg: assuming signed data in 'Python-3.13.0.tar.xz'
gpg: Signature made Mon 07 Oct 2024 08:30:45 BST
gpg:       using RSA key 7169605F62C751356D054A26A821E680E5FA6305
gpg: Good signature from "Thomas Wouters <thomas@python.org>" [unknown]
gpg:        aka "Thomas Wouters <thomas@xs4all.nl>" [unknown]
gpg:        aka "Thomas Wouters <twouters@google.com>" [unknown]
gpg:        aka "Thomas Wouters <thomas.wouters.prive@gmail.com>" [unknown]
gpg:        aka "Thomas Wouters <thomaswout@gmail.com>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:       There is no indication that the signature belongs to the owner.
Primary key fingerprint: 7169 605F 62C7 5135 6D05 4A26 A821 E680 E5FA 6305

$


10月7日にサインされていますね。
3.12.6 の時と同じように make してみます。

$ tar xvf Python-3.13.0.tar.xz
$ cd Python-3.13.0
$ ./configure --prefix=/usr/local/python --enable-optimizations --with-readline --enable-shared

$ make    (52分かかりました。)
・・・・・・
Total duration: 6 min 52 sec
Total tests: run=9,361 skipped=201
Total test files: run=44/44
Result: SUCCESS
true
・・・・・・
The necessary bits to build these optional modules were not found:
_dbm
To find the necessary bits, look in configure.ac and config.log.

Checked 112 modules (33 built-in, 77 shared, 1 n/a on linux-aarch64, 0 disabled, 1 missing, 0 failed on import)
make[1]: Leaving directory '〇〇〇〇〇〇/Python-3.13.0'
$

$ make test (今回はテストも行ってみました。)
・・・・・・
21 tests skipped:
   test.test_asyncio.test_windows_events
   test.test_asyncio.test_windows_utils test.test_gdb.test_backtrace
   test.test_gdb.test_cfunction test.test_gdb.test_cfunction_full
   test.test_gdb.test_misc test.test_gdb.test_pretty_print
   test_android test_dbm_ndbm test_devpoll test_free_threading
   test_ioctl test_kqueue test_launcher test_msvcrt test_startfile
   test_winapi test_winconsoleio test_winreg test_winsound test_wmi

4 tests skipped (resource denied):
   test_peg_generator test_tkinter test_ttk test_zipfile64

453 tests OK.

Total duration: 13 min 45 sec
Total tests: run=44,312 skipped=1,693
Total test files: run=474/478 skipped=21 resource_denied=4
Result: SUCCESS
$

# make install

/usr/local/python にインストールされているので、環境変数を設定するのも前回通りです。

$ cat python3.sh
export PATH=/usr/local/python/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/python/lib
$
$ source python3.sh

$ python3 --version
Python 3.13.0
$


これで、Python 3.13.0 のインストールは一応完了です。

https://www.python.org/downloads/release/python-3130/
の中で、(Google Chrome による翻訳)
New features
・ PyPyをベースとした、新しく改良された対話型インタプリタです。複数行の編集や色のサポート、色付きの例外トレースバックが特徴です。


となっていたので、試してみました。

$ python3
Python 3.13.0 (main, Oct 8 2024, 02:36:16) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
(プロンプトやエラーメッセージの一部などがピンク色になった。)
>>> for num in [1,2,3,4]:
...        print(nim)
...
Traceback (most recent call last):
File "<python-input-0>", line 2, in <module>
    print(nim)
    ^^^
NameError: name 'nim' is not defined. Did you mean: 'num'?
>>> for num in [1,2,3,4]:
...        print(nim)
(上矢印キーでいっぺんに表示される。修正も可能!)

>>> for num in [1,2,3,4]:
...        print(num)
...
1
2
3
4
>>>

これは、便利ですね。

さて、New featuresのもう一つ、
・ 予備的、実験的なJITであり、大幅なパフォーマンス向上のための土台となる。

ですが、これは Just In Time コンパイラのことらしいです。これを実現するには、configure のオプション、

--enable-experimental-jit
    build the experimental just-in-time compiler (default is no)


を加える必要があります。試しにこれを加えてmake すると、

$ ./configure --prefix=/usr/local/python --enable-optimizations  --with-readline --enable-shared --enable-experimental-jit
$ make
・・・・
    | raise RuntimeError(f"Can't find {tool}-{_LLVM_VERSION}!")
    | RuntimeError: Can't find clang-18!
   +---------------- ... ----------------
    | and 219 more exceptions
   +------------------------------------
make[2]: *** [Makefile:3029: jit_stencils.h] Error 1
make[2]: Leaving directory '/home/espiya/src/Python-3.13.0-jit'
make[1]: *** [Makefile:889: profile-gen-stamp] Error 2
make[1]: Leaving directory '/home/espiya/src/Python-3.13.0-jit'
make: *** [Makefile:901: profile-run-stamp] Error 2
$

この Raspberry pi ではうまく行きませんでした。残念。
clang はシステムのものでも Ver.16 なので、自分でインストールする必要があります。しかも、clang は LLVM なので、ちょっと大変そうです。
Python って、GCC でなくて、LLVM 推しなのですね。 そのうち試して見たいと思います。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

入門者が生成AIも利用して Python プログラミングを行う。 その4

2024-10-06 16:10:01 | Python
前回までにすべての関数が作成できました。 後は、これらを集めてプログラミングするだけです。

Raspberry Pi が遅いので、最初に考えた検索のループで毎回 fill_list()を使って行リストを作るのは効率が悪そうです。1回だけ実行して、
f_R1,f_R2,f_R3,f_R4,f_R5,f_R6,f_R7,f_R8,f_R9
に代入することにしました。

更に、3行ずつブロックの検査をして、最後に検索するリスト数を減らしてから、列の検査をすることにしました。

プログラムはこんな感じです。

関数定義の部分は省略しました。

print('Question: ')
print_answer(R1,R2,R3,R4,R5,R6,R7,R8,R9)

# 条件を検査して、結果を出力する。
# fill_list()を使うので行の検査は終了している。

f_R1 = fill_list(R1)
f_R2 = fill_list(R2)
f_R3 = fill_list(R3)
f_R4 = fill_list(R4)
f_R5 = fill_list(R5)
f_R6 = fill_list(R6)
f_R7 = fill_list(R7)
f_R8 = fill_list(R8)
f_R9 = fill_list(R9)

right_b1 = []
for row1 in f_R1:
   for row2 in f_R2:
      for row3 in f_R3:
         if chk_block(row1,row2,row3) == False:
            continue
         right_b1.append([row1,row2,row3])
         print(row1)
         print(row2)
         print(row3)
         print('-'*28)
print(' 1-3 blocks')

right_b2 = []
for row4 in f_R4:
   for row5 in f_R5:
      for row6 in f_R6:
         if chk_block(row4,row5,row6) == False:
            continue
         right_b2.append([row4,row5,row6])
         print(row4)
         print(row5)
         print(row6)
         print('-'*28)
print(' 4-6 blocks')

right_b3 = []
for row7 in f_R7:
   for row8 in f_R8:
      for row9 in f_R9:
         if chk_block(row7,row8,row9) == False:
            continue
         right_b3.append([row7,row8,row9])
         print(row7)
         print(row8)
         print(row9)
         print('-'*28)
print(' 7-9 blocks')

print('Answer: ')
for bk1 in right_b1:
   for bk2 in right_b2:
      for bk3 in right_b3:
         if chk_col(bk1[0],bk1[1],bk1[2],bk2[0],bk2[1],bk2[2],bk3[0],bk3[1],bk3[2]) == False:
            continue

         # 答えを出力。複数の答えも想定する。
         print_answer(bk1[0],bk1[1],bk1[2],bk2[0],bk2[1],bk2[2],bk3[0],bk3[1],bk3[2])
         print(' ')

print('=== END ===')

メモリがあふれるかもと思いましたが、それは大丈夫でした。しかし、それでも遅い、最初の答えがでるまで半日、他に答えがないことを調べるため全部の検査にさらに1日くらいかかりました。遅いのでちゃんと動いているか不安になり、3つのブロック検査の途中経過も出力してあります。特に2段目(4-6 blocks) の処理が長かったです。

python3 sudoku.py
Question:
0 9 8 | 0 0 6 | 0 0 4
1 0 4 | 0 0 2 | 0 3 0
3 6 7 | 0 0 1 | 0 0 0
------+-------+-------
0 0 0 | 1 0 0 | 4 9 7
0 0 0 | 0 3 0 | 0 0 0
7 4 2 | 0 0 8 | 0 0 0
------+-------+-------
0 0 0 | 2 0 0 | 9 7 6
0 7 0 | 6 0 0 | 5 0 8
6 0 0 | 5 0 0 | 3 4 0
(途中省略)
[6, 9, 1, 5, 7, 8, 3, 4, 2]
----------------------------
[8, 5, 4, 2, 3, 1, 9, 7, 6]
[3, 7, 2, 6, 9, 4, 5, 1, 8]
[6, 9, 1, 5, 8, 7, 3, 4, 2]
----------------------------
7-9 blocks
Answer:
2 9 8 | 3 5 6 | 7 1 4
1 5 4 | 8 7 2 | 6 3 9
3 6 7 | 4 9 1 | 2 8 5
------+-------+-------
8 3 6 | 1 2 5 | 4 9 7
9 1 5 | 7 3 4 | 8 6 2
7 4 2 | 9 6 8 | 1 5 3
------+-------+-------
5 8 1 | 2 4 3 | 9 7 6
4 7 3 | 6 1 9 | 5 2 8
6 2 9 | 5 8 7 | 3 4 1

=== END ===
$

入門なので、自分で作る関数作成はなるべく単純にしてみました。そんなわけで同じ事を3回も繰り返したりして無駄に長くなっているかもしれませんが、後で分かりやすい事を優先しました。

生成AIも要所で使うことにして、2つの関数生成に使う事ができました。どれを生成AIに任せてどれを自分で作るかは色々と試す必要がありそうです。生成AIの回答の関数は入門者には勉強になる部分も多かったので、ちょっと関数を作るときに気になる事があるなら使って良いのかもしれませんね。途中怪しげなプログラムを提示されました、そこは、回答の通りテストする必要があるわけです。Google Gemini の場合は説明が良いですね。docstring 作成に使った方が良いような気がするのは以前書いた通りでした。

プロンプトに一言も入れていないのに「数独でしょ」と言ってどこかから持ってきた関数を提示するのも面白かったです。

入門者には、生成AIを関数生成とかclass定義とかで活用して、得られたものをテストしたり、その中で知らない機能を使っていたら調べたりする事が経験値を上げるには役立ちそうです。今後生成AIが「この関数はこっちの方が良いですよ。」と言ってくる事は当然ありそうなので、活用したいところです。

Raspberry Pi なので、遅いからスピードアップのために改良するという試行錯誤ができるので、Python入門時には何かと役立ちそうです。
コメント (1)
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

入門者が生成AIも利用して Python プログラミングを行う。 その3

2024-10-05 14:31:04 | Python
前回の Gemini へのプロンプトで最初の「Python で」が抜けていました。

さて、「数独」の3つめ条件、縦9列が1~9までのすべての数字を重複無く含んでいる、を調べる関数
chk_col(c1, c2, c3, c4, c5, c6, c7, c8, c9 ) を定義してみます。
単純に9つの行リストを引数に与えます。
(こちらも gemini は、docstring生成のみに使いました。その中で、Args: Returns: のみ使います。)

def chk_col(c1, c2, c3, c4, c5, c6, c7, c8, c9):
   """
   9つのリストからなる9x9の行列の各列に対して、`check_list()` 関数を適用します。
   全ての列で `check_list()` が `True` を返す場合にのみ、`True` を返します。
   Args:
      c1, c2, ..., c9 (list): 要素数9のリスト。それぞれが9x9行列の1つの列に対応します。
   Returns:
   bool: 全ての列が `check_list()` の条件を満たす場合にTrue、そうでない場合にFalse
   """

   for i in range(0,9):
      column=[c1[i],c2[i],c3[i],c4[i],c5[i],c6[i],c7[i],c8[i],c9[i]]
      if check_list(column) == False:
         return False
   return True

さて、各行の空白の要素を埋めるための関数fill_list()を Google Gemini に作成してもらおうとたところ中々うまく行きませんでした。 記録しなかったので詳しくは書けませんがrandom を使ったり、再帰関数にしたりして答えが正しくない関数を提案されました。

問題は、要素の値が0のインデックスを探して、まだ使っていない数値を求めて、値を埋めていくので、順列ということで、SimplePrograms で出てきた itertools ライブラリを使ってみるのも良さそうだと考えて、次のようなプロンプトにしました。

「Python で、要素数9のリストを A とします。A の各要素の値は1から9までの整数でかつ値が重複できないという条件があります。
今、A のいつかの要素に値が設定されており、まだ値が設定されていない要素の値は 0 になっているとします。この A を引数にして、A の値が0 の所に数値をいれて条件を満たすすべてのリストを求め、それぞれのリストを要素とするリストを返却するような関数fill_list(A) を作成してください。必要なら itertools ライブラリを使ってください。」

Gemini の回答は、以下の様にねらいどおりでした。(それが良いかどうかは別の話)

ここから======
## Pythonでリストを完成させる関数fill_list()の実装

### 問題の理解とアプローチ

* **問題:** 要素数9のリストAがあり、一部の要素に値が設定されている。残りの要素に1から9までの重複のない数値を全てのパターンで割り当て、全ての組み合わせのリストを作成する。
* **アプローチ:**
1. **未設定要素のインデックス:** まず、Aの中で値が0の要素のインデックスをリストで取得する。
2. **順列の生成:** itertools.permutations()を用いて、未設定要素に割り当てる数値の全ての順列を生成する。
3. **リストの生成:** 各順列に対して、元のリストAのコピーを作成し、未設定要素のインデックスに順列の値を順に代入していく。
4. **全ての組み合わせのリスト:** 生成された全てのリストを要素とするリストを返す。

### コードの実装

```python
import itertools

def fill_list(A):
   """
   Aの値が0の所に数値をいれて条件を満たすすべてのリストを返す関数
   Args:
      A: 要素数9のリスト。一部の要素に1から9までの値が設定されている。
   Returns:
      list: 条件を満たす全てのリストのリスト
   """
   # 未設定要素のインデックスを取得
   unset_indices = [i for i, x in enumerate(A) if x == 0]
   # 未設定要素に割り当てる数値の候補
   numbers = [i for i in range(1, 10) if i not in A]
   # 全ての順列を生成
   permutations_list = list(itertools.permutations(numbers))
   # 全ての組み合わせのリストを作成
   result = []
   for perm in permutations_list:
      new_list = A.copy()
      for i, num in zip(unset_indices, perm):
         new_list[i] = num
   result.append(new_list)

   return result
```

### コードの説明

* `unset_indices`: Aの中で値が0の要素のインデックスをリストで取得します。
* `numbers`: Aにまだ含まれていない1から9までの数値をリストで取得します。
* `permutations_list`: itertools.permutations()を用いて、numbersの全ての順列を生成し、リストに変換します。
* `result`: 最終的に返却する、全ての組み合わせのリストを格納する空のリストです。
* `new_list`: Aのコピーを作成し、ここに順列の値を代入していきます。
* `zip(unset_indices, perm)`: unset_indicesとpermの要素を同時に取り出すためにzip関数を使用しています。

### 使用例

```python
A = [1, 0, 3, 0, 5, 6, 0, 8, 9]
result = fill_list(A)
print(result)
```

このコードを実行すると、Aの0の要素に可能な全ての組み合わせで数値を代入したリストのリストが得られます。

### 注意点

* リストの長さが9であるという前提で実装されています。
* 0以外の数値が重複して含まれている場合は、意図した結果にならない可能性があります。
* リストの要素数が大きくなると、生成される組み合わせの数が膨大になり、計算時間が長くなる可能性があります。

このコードは、与えられた条件のもとで、Aの全ての組み合わせを効率的に生成することができます。

======ここまで


念のため、prog04.py というファイルを作って、テストします。最初に fill_list()を回答の通り定義して、テスト部分は、

A = [6, 2, 0, 5, 0, 7, 3, 4, 0]
print(A)
print('-'*28)

result = fill_list(A)

for l in result:
print(l)

です。

$ python3 prog04.py
[6, 2, 0, 5, 0, 7, 3, 4, 0]
----------------------------
[6, 2, 1, 5, 8, 7, 3, 4, 9]
[6, 2, 1, 5, 9, 7, 3, 4, 8]
[6, 2, 8, 5, 1, 7, 3, 4, 9]
[6, 2, 8, 5, 9, 7, 3, 4, 1]
[6, 2, 9, 5, 1, 7, 3, 4, 8]
[6, 2, 9, 5, 8, 7, 3, 4, 1]
$

正しい答えが得られるようになりました。
この回答の中では、SimplePrograms で学んだ enumerate()、リスト内包表記、docstring が使われています。
出てこなかった、リストデータ型のメソッド append() と組み込み関数 zip() を調べて見ました。
https://docs.python.org/ja/3/tutorial/datastructures.html

list.append(x)

   リストの末尾に要素を一つ追加します。a[len(a):] = [x] と等価です。


https://docs.python.org/ja/3/library/functions.html#zip

zip(*iterables, strict=False)

   複数のイテラブルを並行に反復処理し、各イテラブルの要素からなるタプルを生成します。

   以下はプログラム例です:
   >>>

for item in zip([1, 2, 3], ['sugar', 'spice', 'everything nice']):
   print(item)

(1, 'sugar')
(2, 'spice')
(3, 'everything nice')

より正式な定義: zip() は、 i 番目のタプルが 引数に与えた各イテラブルの i 番目の要素を含むような、タプルのイテレータを返します。


itertoolsライブラリでは、permutations()を使っています。
https://docs.python.org/ja/3/library/itertools.html#itertools.permutations

itertools.permutations(iterable, r=None)

   Return successive r length permutations of elements from the iterable.

   r が指定されない場合や None の場合、r はデフォルトで iterable の長さとなり、可能な最長の順列の全てが生成されます。


この問題にぴったりの組合せイテレータですね。

最後にを出力するときにちょっと数独ぽくしたいので、すべての条件を満たした答えを出力する関数print_answer() を作成します。これは問題の出力にも使えますね。(docstring は Gemini によります。)

def print_answer(a,b,c,d,e,f,g,h,i):
   """Prints a 9x9 Sudoku puzzle in a formatted manner.
   Args:
      a, b, c, d, e, f, g, h, i: Lists representing the rows of the Sudoku puzzle.
   """

   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]))
   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7],b[8]))
   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(c[0],c[1],c[2],c[3],c[4],c[5],c[6],c[7],c[8]))
   print('------+-------+-------')
   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(d[0],d[1],d[2],d[3],d[4],d[5],d[6],d[7],d[8]))
   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(e[0],e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8]))
   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7],f[8]))
   print('------+-------+-------')
   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(g[0],g[1],g[2],g[3],g[4],g[5],g[6],g[7],g[8]))
   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(h[0],h[1],h[2],h[3],h[4],h[5],h[6],h[7],h[8]))
   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(i[0],i[1],i[2],i[3],i[4],i[5],i[6],i[7],i[8]))

何故、Gemini の回答である docstring が英文になったのでしょうか?
これは、Gemini が次のようにこの関数の修正案を提示したからです。入門者にとって衝撃の答えでした。

ここから======(空行は省略)
I'd be glad to provide a response that combines the strengths of both Response A and Response B, addressing their potential shortcomings and incorporating insights from the ratings:

**Improved Response:**
```python
def print_answer(a, b, c, d, e, f, g, h, i):
   """Prints a 9x9 Sudoku puzzle in a formatted manner.
   Args:
      a, b, c, d, e, f, g, h, i: Lists representing the rows of the Sudoku puzzle.
   """
   # Define a separator string for better readability
   separator = "------+-------+-------"
   # Print the puzzle rows with appropriate formatting
   for row in [a, b, c, d, e, f, g, h, i]:
      print(" ".join(row))
      print(separator)
   # Print the final row without the separator
   print(" ".join(i))
```
**Explanation:**
- **Clear Function Definition:** The function is defined with a clear name (`print_answer`) and a docstring that explains its purpose and parameters.
- **Concise and Efficient:** The code is concise and efficient, using a loop to iterate over the rows and a list comprehension to join row elements with spaces.
- **Readability:** The use of a separator string improves readability by visually separating the rows and columns.
- **Flexibility:** The code can be easily adapted to print different puzzle sizes by modifying the separator string and the loop iteration.
- **Correctness:** The code accurately prints the Sudoku puzzle in the desired format.
**Key Improvements:**
- **Combined Strengths:** The response incorporates the best aspects of Response A (conciseness, efficiency) and Response B (clear function definition, docstring).
- **Enhanced Readability:** The separator string enhances visual clarity.
- **Flexibility:** The code is adaptable to different puzzle sizes.

I believe this improved response effectively addresses the prompt and provides a valuable solution.
======ここまで


Gemini は、この関数を数独パズルのためと評価して、その出力ならこの関数をと言うわけですね。
Gemini でこの回答を日本語に訳したものも記録しておきます。(空行は省略)

ここから======
**日本語訳:**
```
def print_answer(a, b, c, d, e, f, g, h, i):
   """9x9の数のパズル (数独) をきれいに表示する関数です。
   引数:
      a, b, c, d, e, f, g, h, i: 数独のパズルの各行を表すリストです。
   """
   # 行と行の間の区切り線として使う文字列
   separator = "------+-------+-------"
# パズルの各行をきれいに表示
   for row in [a, b, c, d, e, f, g, h, i]:
      print(" ".join(row)) # リストの要素をスペースで繋いで表示
      print(separator) # 区切り線を表示
   # 最後の行は区切り線を表示せずに表示
   print(" ".join(i))
```
**コードの説明:**
* **関数 `print_answer`:** 数独のパズルをきれいに表示するための関数です。
* **引数:**
   * `a`, `b`, `c`, ... , `i`: 数独のパズルの各行を表すリストです。それぞれのリストの要素は、その行の数字に対応します。
* **`separator`:** 行と行の間の区切り線として使う文字列です。
* **`for` ループ:** パズルの各行 (`a`, `b`, ... , `i`) を順番に処理します。
* **`print(" ".join(row))`:** リスト `row` の要素をスペースで繋いで一つの文字列にし、それを表示します。これにより、一行の数字が綺麗に表示されます。
* **`print(separator)`:** 区切り線を表示します。
* **最後の `print(" ".join(i))`:** 最後の行は区切り線を表示せずに表示します。
**このコードは、与えられた9つのリスト (各リストが数独の一行を表す) を使って、数独のパズルをコンソール上に綺麗に表示します。**
### より詳しい説明
* **数独:** 9×9のマス目に1から9までの数字を重複なく入れるパズルです。各行、各列、および3×3のブロック内に同じ数字が重複してはいけません。
* **リスト:** Pythonで複数の要素を順番に格納するデータ構造です。このコードでは、各リストが数独の一行に対応しています。
* **`join` メソッド:** リストの要素を指定された文字列で繋いで一つの文字列にするメソッドです。このコードでは、スペースで繋いでいます。
**このコードのポイント:**
* **可読性:** 変数名やコメントによって、コードの意味が分かりやすくなっています。
* **効率性:** `for` ループを使って簡潔に記述されています。
* **汎用性:** 数独のサイズを変更したり、区切り線の文字を変更したりするなど、少し変更するだけで他のパズルにも適用できます。

**もし、数独のパズルを解くプログラムを作りたい場合は、このコードをベースに、数字の配置のルールや解法アルゴリズムを実装する必要があります。**
======ここまで


生成AIを利用したプログラミングは、なかなか興味深いですね。
Gemini の回答に驚いて非常に長くなったので、続きは次回に投稿します。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

入門者が生成AIも利用して Python プログラミングを行う。 その2

2024-10-04 20:49:43 | Python
前回の続きで、具体的なプログラミングを行ってみます。

まず、Google Gemini を使って9要素のリストが1~9のすべての数字を含んでいるかどうかを調べる関数check_list()を作ります。

Gemini へのプロンプトは、以下の通りです。

「要素数9のリスト b を引数として、
リストの各要素が重複しないで1から9の値になっているかどうかを調べて、この条件を満たすときは真、満たさない場合は偽を返すような関数 check_list(b)を作成してください。」

Gemini の回答: (抜粋)
## リストの要素が重複なく1から9まで含まれているか判定する関数

def check_list(b):
   """
   リストbの要素が重複なく1から9まで含まれているかを判定する関数
   Args:
      b: 判定対象のリスト
   Returns:
      bool: 条件を満たす場合はTrue、満たさない場合はFalse
   """

   # 要素数が9でない場合、Falseを返す
   if len(b) != 9:
      return False

   # 要素が1から9の範囲に含まれていないか、重複があるかを確認
   return set(b) == set(range(1, 10))

### コード解説

1. **関数定義:**
   * `check_list(b)`: リスト `b` を引数にとる関数。
2. **要素数チェック:**
   * `if len(b) != 9:`: リストの長さが9でない場合、条件を満たさないためFalseを返す。
3. **重複と範囲チェック:**
   * `return set(b) == set(range(1, 10))`:
      * `set(b)`: リスト `b` を集合に変換することで、重複を自動的に除去する。
      * `set(range(1, 10))`: 1から9までの数値の集合を作成する。
      * `==`: 両方の集合が等しいか(要素が完全に一致するか)を比較する。
      * 集合が等しい場合、リスト `b` の要素が重複なく1から9まで含まれていることを意味するため、Trueを返す。

### 動作原理

* **集合の利用:** 集合は重複した要素を持たないため、リストを集合に変換することで、要素の重複を簡単に検出できる。
* **範囲の比較:** `range(1, 10)` で生成した集合と、リスト `b` から作成した集合を比較することで、要素が1から9までの範囲に含まれているか、かつ重複がないかを同時に確認できる。

=====ここまで


docstring 付きですね。数字が順番に並んでいないので、set()を使ってリストを集合に替えて判定するのですね。
解説も分かりやすく、そのまま利用できます。

次に、「数独」の9個のブロックが1から9の数値を重複無く含んでいるかを調べるために、
3行毎に3回に分けて調べるようにします。一つでも False になると直ぐに終了して次に行くのと、長くなりそうなのでこうしてみました。

そのために、3つの行を引数にして条件を調べる chk_block(a,b,c) という関数を作ってみます。
これは、生成AIを使わないで、単純に次のようにしました。
(Gemini は、docstring作成にのみ使いました。Raises:とExamples:は省略)

def chk_block(a,b,c):
   """
   3つのリストを9要素ずつに分割し、各ブロックに対して `check_list` 関数を適用します。
   全てのブロックで `check_list` が `True` を返す場合にのみ、`True` を返します。
   Args:
      a (list): 要素数9のリスト
      b (list): 要素数9のリスト
      c (list): 要素数9のリスト
   Returns:
      bool: 全てのブロックが `check_list` の条件を満たす場合にTrue、そうでない場合にFalse
   """


   block1 = a[0:3] + b[0:3] + c[0:3]
   block2 = a[3:6] + b[3:6] + c[3:6]
   block3 = a[6:] + b[6:] + c[6:]

   if check_list(block1) == False:
      return False
   if check_list(block2) == False:
      return False
   if check_list(block3) == False:
      return False
   return True

この2つの関数をテストします。
ファイル prog-01.py で、2つの関数を定義して、その下に以下のテストプログラムを挿入して実行します。

row1 = [1, 2, 3, 4, 5, 6, 7, 8, 9 ]
row2 = [4, 5, 6, 7, 8, 9, 1, 2, 3 ]
row3 = [7, 8, 9, 1, 2, 3, 4, 5, 6 ]

if chk_block(row1, row2, row3):
print("OK")
else:
print("NG")

row1 = [1, 2, 3, 4, 5, 6, 7, 8, 9 ]
row2 = [4, 5, 6, 7, 8, 9, 1, 7, 3 ]
row3 = [7, 8, 9, 1, 2, 3, 4, 5, 6 ]

if chk_block(row1, row2, row3):
print("OK")
else:
print("NG")

row1 = [1, 2, 3, 4, 5, 6, 7, 8, 9 ]
row2 = [4, 5, 6, 7, 2, 9, 1, 2, 3 ]
row3 = [7, 8, 9, 1, 2, 3, 4, 5, 6 ]

if chk_block(row1, row2, row3):
print("OK")
else:
print("NG")

row1 = [1, 2, 3, 4, 5, 6, 7, 8, 9 ]
row2 = [4, 5, 6, 7, 8, 9, 1, 2, 3 ]
row3 = [7, 8, 1, 1, 2, 3, 4, 5, 6 ]

if chk_block(row1, row2, row3):
print("OK")
else:
print("NG")

$ python3 prog01.py
OK
NG
NG
NG
$

テストで問題ないことが分かりました。
長くなりましたので、続きはまた後で投稿します。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

入門者が生成AIも利用して Python プログラミングを行う。 その1

2024-10-03 21:32:06 | Python
SimplePrograms を利用して、Pythonの入門も大体終了したので、実際にプログラミングしてみます。

プログラムしようと思ったのは「数独」です。

スマートフォンを使っていると、この問題は解けるか?という宣伝広告が出てくることがあります。いくつか候補を試して進めるしかないような場合があって鬱陶しいので、それをプログラムで解いてしまおうとする試みです。本当に解が1つしかないのかなども試してみたいです。

今回は、問題を与えて解を求めるように作ってみます。最近、生成AIによるプログラミングも話題になっているので使ってみたいと思います。と言っても、全部作ってもらうと入門にならないので、手順はあらかじめ考えておいて、それを実現するような関数を生成AIに作ってもらい実現してみようと思いました。

考えた手順はこんな感じです。

初期化)
1)マスを横方向にみて、9個の要素を含むリストとみなし、上からR1、R2、...、R9 とします。
 各リストには1~9の数字が重複しないで入っている必要があります。
2)空白は0、初期の数字はそのまま(1から9の整数)R1~R9の各要素に代入して初期値にします。

数値挿入)
3)このリストrの0の要素に、残りの数値を入れたリスト(複数できる)をすべて生成するような
関数 fill_list(r)
を作ります。その返却値は、一つの要素が、要素数9個のリストである、リストです。

条件判定)
4)引数で与えたリストの要素が1)の条件を満たすリストかどうかを判定する
関数 check_list(list)
を作ります。
各行は条件をすでに条件をみたしているので、ブロックと列の判定にこれを使います。

答えを探す)
5)話を単純にするために、9重のforループを作り、条件を満たすかどうかを check_list() で判定します。こんな感じでしょうか。

for row1 in fill_list(R1):
   for row2 in fill_list(R2):
      for row3 in fill_list(R3):
         for row4 in fill_list(R4):
            for row5 in fill_list(R5):
               for row6 in fill_list(R6):
                  for row7 in fill_list(R7):
                     for row8 in fill_list(R8):
                        for row9 in fill_list(R9):

 チェックする量が膨大ですが、そのほとんどが条件を満たさないので、満たさなかったらcontinue文を使って直ぐ次に行くようにしたほうが良さそうです。答えが2つ以上ある場合もありそうですから。

列の判定)
6)列の判定は、row1 ~ row9 の各要素を先頭から取り出してマスの縦方向のカラムを表す
9個のリストに対してcheck_list()で判定して、
どれかが偽なら次のループへ、
すべてが真なら列は条件を満たしていることになります。

ブロックの判定)
7)ブロックの判定は、数独の9つのブロックをリストにして、そのリストに対してcheck_list()を実施して、
どれかが偽なら次のループへ、
すべてが真ならブロックの条件を満たしてることになります。
ループの一番内側で使うより、3つの行毎に判定するのが良さそうです。最初の3ブロックで条件を満たさなければ残りの6行を用意して判定する必要はありませんから。

6)、7)の順番は自由です。

こんな感じです。

生成AIは、各関数を作成するのに利用してみます。
上の手順の中で、check_list() と fill_list()の関数を作成してもらいたいと思います。

長くなるので、今後分割して投稿したいと思います。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Raspberry Pi に Python 3.12.6 をインストールする

2024-09-20 15:40:08 | Python
前回 Raspberry Pi の Pi OS も再インストールできたので、Python3 をインストールしてみます。

Python 3.12.6 が、日本時間 2024-09-07 にリリースされていました。

3.12.6 は最新のメンテナンス リリースで、3.12.5 以降の約 90 のバグ修正、ビルドの改善、ドキュメントの変更が含まれています。これは、次のセキュリティ問題に対処するための緊急リリースです。

早速ダウンロードしてみましょう。

$ wget https://www.python.org/ftp/python/3.12.6/Python-3.12.6.tar.xz

PGP署名ファイルもダウンロードします。

$ wget https://www.python.org/ftp/python/3.12.6/Python-3.12.6.tar.xz.asc

壊れたOSのユーザファイルは元に戻してあり、公開鍵はすでにインポートしているので、確認します。

$ gpg --verify Python-3.12.6.tar.xz.asc
gpg: assuming signed data in 'Python-3.12.6.tar.xz'
gpg: Signature made Fri 06 Sep 2024 21:33:01 BST
gpg:        using RSA key 7169605F62C751356D054A26A821E680E5FA6305
gpg: Good signature from "Thomas Wouters <thomas@python.org>" [unknown]
gpg:        aka "Thomas Wouters <thomas@xs4all.nl>" [unknown]
gpg:        aka "Thomas Wouters <twouters@google.com>" [unknown]
gpg:        aka "Thomas Wouters <thomas.wouters.prive@gmail.com>" [unknown]
gpg:        aka "Thomas Wouters <thomaswout@gmail.com>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:     There is no indication that the signature belongs to the owner.
Primary key fingerprint: 7169 605F 62C7 5135 6D05 4A26 A821 E680 E5FA 6305
$


早速、以前行ったこちらのようにインストールしてみます。
https://blog.goo.ne.jp/espiya/e/fdf200d42183170e14bd374987749a7d

まずは、関連パッケージを apt でインストールします。

# apt install libncurses-dev libreadline-dev libssl-dev libssh-dev
#
    ( libssh-dev は、念のため )

# apt install libsqlite3-dev libbz2-dev libffi-dev libgdbm-dev liblzma-dev tcl tcl-dev tk tk-dev uuid-dev zlib1g-dev
#

早速 make してみましょう。

$ tar xvf Python-3.12.6.tar.xz
$ cd Python-3.12.6
$ ./configure --prefix=/usr/local/python --enable-optimizations  --with-readline --enable-shared
$ make
$


_dbm がないのは前回通りです。

make できたら、インストールします。インストール先の /usr/local/python は、無ければ生成してくれました。

# make install

Python3 を使うための環境設定は、 環境変数を export するところを python3.sh というファイルにして、Bash の場合次のように設定します。

$ cat python3.sh
export PATH=/usr/local/python/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/python/lib
$

$ source python3.sh


早速実行してみました。

$ python3
Python 3.12.6 (main, Sep 14 2024, 16:22:55) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>>
$
$ pip3 --version
pip 24.2 from /usr/local/python/lib/python3.12/site-packages/pip (python 3.12)
$


これで、最新の Python3 がインストールできました。

壊れてしまったRaspberry Pi のシステムもほぼ元通りになり、10月に 3.13 がリリースされる予定なので、その準備もできましたね。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ その21

2024-09-07 21:28:15 | Python
SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms

の 21番目のプログラムは、33行の数字当てゲームです。

import random

guesses_made = 0

name = input('Hello! What is your name?\n')

number = random.randint(1, 20)
print ('Well, {0}, I am thinking of a number between 1 and 20.'.format(name))

while guesses_made < 6:

    guess = int(input('Take a guess: '))

    guesses_made += 1

    if guess < number:
       print ('Your guess is too low.')

    if guess > number:
       print ('Your guess is too high.')

    if guess == number:
       break

if guess == number:
    print ('Good job, {0}! You guessed my number in {1} guesses!'.format(name, guesses_made))
else:
    print ('Nope. The number I was thinking of was {0}'.format(number))


例によって、prog-33.py というファイルにして実行してみます。

$ python3 prog-33.py
Hello! What is your name?
espiya
Well, espiya, I am thinking of a number between 1 and 20.
Take a guess: 5
Your guess is too low.
Take a guess: 10
Your guess is too high.
Take a guess: 8
Your guess is too high.
Take a guess: 6
Good job, espiya! You guessed my number in 4 guesses!
$


6回までに1から20までの数を当てるゲームですね。

6回で当てられないと、
Nope. The number I was thinking of was 6
$

となります。

さすがに最後のプログラムとなると新しい機能はほとんど使われていませんね。

その中で、random モジュールが使われています。
https://docs.python.org/ja/3/library/random.html#module-random

このモジュールでは様々な分布をもつ擬似乱数生成器を実装しています。

整数用に、ある範囲からの一様な選択があります。シーケンス用には、シーケンスからのランダムな要素の一様な選択、リストのランダムな置換をインプレースに生成する関数、順列を置換せずにランダムサンプリングする関数があります。

実数用としては、一様分布、正規分布 (ガウス分布)、対数正規分布、負の指数分布、ガンマおよびベータ分布を計算する関数があります。角度の分布を生成するにはフォン・ミーゼス分布が利用できます。

ほとんど全てのモジュール関数は、基礎となる関数 random() に依存します。この関数はランダムな浮動小数点数を半開区間 0.0 <= X < 1.0 内に一様に生成します。Python は中心となる乱数生成器としてメルセンヌツイスタを使います。これは 53 ビット精度の浮動小数点を生成し、周期は 2**19937-1 です。本体は C で実装されていて、高速でスレッドセーフです。メルセンヌツイスタは、現存する中で最も広範囲にテストされた乱数生成器のひとつです。しかしながら、メルセンヌツイスタは完全に決定論的であるため、全ての目的に合致しているわけではなく、暗号化の目的には全く向いていません。



以前、LINEのAIチャット君の紹介で出てきたように、random()の乱数生成方法はメルセンヌツイスタを使っているのですね。注意書きにもあるように、メルセンヌツイスタは決定論的なので、使用するシードに工夫が必要ですが、生成される乱数はOS提供の乱数に比べれば非常に良いと考えられるので、乱数が必要なプログラムでは十分使えますね。この中で、


random.randint(a, b)

a <= N <= b であるようなランダムな整数 N を返します。randrange(a, b+1) のエイリアスです。



を使って、1から20までの乱数を生成して答えとするわけです。

クイズなので、input() で入力を、print() で出力をするプログラムですね。
整数入力の時は、 int(input(.....)) と覚えておきます。

while ループ内の if 文ですが、elif や continue を使いたいと思う気がしますが、
SimplePrograms ではいずれも使っていませんでした。

さて、出力ですが、

https://docs.python.org/ja/3.8/tutorial/inputoutput.html#input-and-output
によると、


・ フォーマット済み文字列リテラル を使うには、開き引用符や三重の開き引用符の前に f あるいは F を付けて文字列を始めます。 この文字列の内側では、文字 { と文字 } の間に Python の式が書け、その式から変数やリテラル値が参照できます。

・ 文字列の str.format() メソッドは、もう少し手間がかかります。 ここでも { と } を使って変数に代入する場所の印を付けて、細かいフォーマットの指示を出せますが、フォーマットされる対象の情報を与える必要があります。

フォーマット済み文字リテラル (短くして f-string とも呼びます) では、文字列の頭に f か F を付け、式を {expression} と書くことで、 Python の式の値を文字列の中に入れ込めます。



これを、いつくか試してみます。

>>> a = 10
>>> b = 100
>>>
>>> print(f'A is {a}, B is {b}.')
A is 10, B is 100.
>>>
>>> print('A is {a00}, B is {b00}.'.format(a00=a, b00=b))
A is 10, B is 100.
>>>
>>> print('A is {0}, B is {1}.'.format(a, b))
A is 10, B is 100.
>>>
>>> print('A is {a}, B is {b}.'.format(a, b))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'a'
>>>
>>> print('A is {a}, B is {b}.'.format(a = a, b = b))
A is 10, B is 100.
>>>


エラーになる例は間違えそうなので、一番最後の例の形で覚えておくことにします。

SimplePrograms では、f-string が出てこなくて、str.format() のみ使っているので、
何かあるのかもしれませんね。

以上、SimplePrograms の21の各プログラムを見てきましたが、入門としてはちょっと変わった?機能を使うことがあり、これを Python プログラミングで使った方が良いという考え方があるような気がしました。特に入門時に docstring を使えるようにする(ちょっと大変と思うなら、生成AIを使う)ことは、プログラミングに有用だと感じました。

今後も何かあれば、Python について試して見たいと思います。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ その20

2024-09-01 09:54:16 | Python
SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms

の 20番目のプログラムは、 28行の2度目の8クイーン問題で、独自の例外(exception)の定義です。

BOARD_SIZE = 8

class BailOut(Exception):
    pass

def validate(queens):
    left = right = col = queens[-1]
    for r in reversed(queens[:-1]):
       left, right = left-1, right+1
       if r in (left, col, right):
          raise BailOut

def add_queen(queens):
    for i in range(BOARD_SIZE):
       test_queens = queens + [i]
       try:
          validate(test_queens)
          if len(test_queens) == BOARD_SIZE:
             return test_queens
          else:
             return add_queen(test_queens)
       except BailOut:
          pass
    raise BailOut

queens = add_queen([])
print (queens)
print ("\n".join(". "*q + "Q " + ". "*(BOARD_SIZE-q-1) for q in queens))


再び、8クイーン問題を扱うプログラムですが、答えは1つだけ表示されます。早速これを prog-28.py というファイルにして実行してみます。

$ python3 prog-28.py
[0, 4, 7, 5, 2, 6, 1, 3]
Q . . . . . . .
. . . . Q . . .
. . . . . . . Q
. . . . . Q . .
. . Q . . . . .
. . . . . . Q .
. Q . . . . . .
. . . Q . . . .
$


最初に、Google Gemini による解説を抜粋します。

ここから========>
### コードの解説

#### 定数と例外
* **BOARD_SIZE:** チェスボードのサイズを定義しています。このプログラムでは、8×8のチェスボードを想定しています。
* **BailOut:** 探索中に矛盾が生じた場合に発生させるカスタム例外です。

#### 関数
* **validate(queens):**
* 引数として、既に配置されたクイーンの列番号のリストを受け取ります。
* 新たに追加しようとしているクイーンと、既に配置されているクイーンが互いに攻撃できるかどうかを検証します。
* 攻撃できる場合は、BailOut例外を発生させます。
* **add_queen(queens):**
* 再帰的な関数で、チェスボードの次の行にクイーンを配置する場所を探索します。
* すべての行にクイーンが配置できたら、その配置のリストを返します。
* 配置できない場合は、BailOut例外を発生させます。

#### メインロジック
* **queens = add_queen([]):**
* 空のリストから始めて、add_queen関数で全てのクイーンを配置します。
* **print(queens):**
* 求められたクイーンの配置(列番号のリスト)を出力します。
* **print("\n".join(". "*q + "Q " + ". "*(BOARD_SIZE-q-1) for q in queens)):**
* 求められたクイーンの配置を、チェスボードの形で視覚的に表示します。"Q"がクイーンを表し、"."が空のマスを表します。

### プログラムの仕組み
1. **add_queen**関数は、チェスボードの最初の行から始めて、各行にクイーンを配置できるかどうかを順に試します。
2. **validate**関数は、新しいクイーンを追加するたびに、そのクイーンが他のクイーンと攻撃できるかどうかを検証します。
3. 攻撃できる場合は、**BailOut**例外が発生し、**add_queen**関数は別の配置を試します。
4. 全ての行にクイーンが配置できたら、その配置が一つ見つかったことになるので、探索を終了します。
5. もし、全ての可能性を試しても有効な配置が見つからなければ、**BailOut**例外が発生し、プログラムはエラーで終了します。

### より詳しく
* **再帰:** このプログラムでは、**add_queen**関数が自分自身を呼び出す再帰的な構造になっています。再帰は、問題をより小さな部分問題に分割し、それらを解決することで全体の問題を解決する手法です。
* **バックトラッキング:** 探索中に矛盾が生じたら、一つ前の状態に戻って別の選択肢を試す手法をバックトラッキングといいます。このプログラムでも、**BailOut**例外が発生した際に、一つ前の状態に戻って探索を続けることで、バックトラッキングが行われています。

ここまで========>

Gemini もこのように、プログラムの概要は教えてくれますね。気になるところを見ていきます。

まず、クラス BailOutの定義が、「カスタム例外」(独自の例外:your own exceptions)ですね。
https://docs.python.org/ja/3/tutorial/controlflow.html#pass-statements

pass 文は何もしません。 pass は、文を書くことが構文上要求されているが、プログラム上何の動作もする必要がない時に使われます:

これは最小のクラスを作るときによく使われる方法です:


ということで、ここで言う「最小のクラス」を定義しているようです。何もしないのが重要ですね。
これは、validate() 関数(基本的には前に出てきた18lines プログラムのunder_attack()関数と同じもの)の中で使われている、raise文
https://docs.python.org/ja/3/reference/simple_stmts.html#the-raise-statement

と関連しており、

raise は最初の式を、例外オブジェクトとして評価します。これは、 BaseException のサブクラスまたはインスタンスでなければなりません。クラスなら、例外インスタンスが必要なとき、クラスを無引数でインスタンス化することで得られます。

例外の 型 は例外インスタンスのクラスで、 値 はインスタンスそのものです。

となって、if 文の条件が真ならば、例外 BailOut が発生するのですね。

その例外を受けるものがないと、普通のエラーみたいになる?ので、validdate()を中で呼び出す、
add_queen()関数の定義の中で 8lines プログラムのように、try ~ except を使って、
この例外を処理するようです。

つまり、validate()で例外BailOutが発生すると except節が実行されて、何もせずにfor文の次の i が代入されて繰り返すようです。validate()で例外が発生しなければ、test_queens の長さが、
8になるまではadd_queen()が再帰的に呼ばれて、
8になったら test_queens を返す
ので、実行例のように最初に見つかった配置を返すのでしょう。

add_queen()の最後の raise 文は念のためのような気がします。これがないと返却値がないことになるので、その判定が必要になるからだと思います。というわけで、適当に簡略化すると、

class カスタム例外:
    ....

def func_test(....):
    for
       ...
       try:
            ...
       except カスタム例外:
            ...
    raise カスタム例外


となるのが、入門者が覚えた方が良い形でしょうか。

最後は、add_queen()で生成された解 queens を出力して、それを使ってジェネレータ式でチェス盤を出力するのですね。 改行文字を頭に付けるのが面白いですね。

join()の使い方をみるために少し試してみました。分かりやすくするために改行文字を"++"にしました。
まず、ジェネレータ式ではない場合です。

>>> q = 3
>>> print("++".join(". "*q + "Q " + ". "*(8-q-1)))
.++ ++.++ ++.++ ++Q++ ++.++ ++.++ ++.++ ++.++


join()の中の文字列「1文字ずつ」に"++"を付加するのですね。つまり単純化すると、

>>> print("++".join("ABCDE"))
A++B++C++D++E
>>>


です。

これをプログラムのようにジェネレータ式にすると、

>>> queens = [2,3,4,6]
>>> print("++".join(". "*q + "Q " + ". "*(8-q-1) for q in queens))
. . Q . . . . . ++. . . Q . . . . ++. . . . Q . . . ++. . . . . . Q .
>>>


join()の使い方と共に、この事例は覚えておいて損はなさそうです。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする