coLinux日記

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

Raspberry Pi に最新の Python3 をインストールする その2

2024-07-24 21:13:49 | Python
前々回で、今使っている Raspberry Pi に Python 3.12.4 をインストールしましたが、
矢印キーが使えないことが分かりました。readline が動いていないからだと思います。

また、 pip3 でモジュールをインストールする時に、
https://pypi.org/simple や  https://www.piwheels.org/simple
からダウンロードするので やっぱり OpenSSL も必要みたいです。

早速 Python 3.12.4 を再インストールしてみましょう。

まず、readline 、ncurses 、openssl の開発環境? をインストールします。 

# apt install libncurses-dev ncurses-doc
#
# apt install libreadline-dev readline-doc
#
# apt install libssl-dev libssl-doc libssh-dev
#

    ( libssh-dev は、念のため )

さらに、関連していると思われるパッケージの開発環境(*-dev) もついでにインストールします。

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

(もう使っていないというメッセージが出てるので、libcamera0.2 を削除します。)

再インストールの邪魔にならないように一度 /usr/local/python をすべて消して作り直します。

# rm -rf /usr/local/python
# mkdir /usr/local/python

前回とほぼ同様に make します。 --with-readline オプションと --enable-shared オプションを新たに付けます。

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


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 111 modules (31 built-in, 78 shared, 1 n/a on linux-aarch64, 0 disabled, 1 missing, 0 failed on import)

と、なったので、 _dbm 以外はすべて解消されました。

# make install

確認のために、環境変数を設定して実行してみます。

$ export PATH=/usr/local/python/bin:$PATH
$ export LD_LIBRARY_PATH=/usr/local/python/lib

$ python3
Python 3.12.4 (main, Jul 24 2024, 09:59:01) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>


矢印キーの動作を確認したところ、無事に動作しました。これができないと、色々試すときに履歴が使えないので不便ですから、初心者には必要な機能ですね。

pip3 も試すために pip 自身をアップデートしてみます。

# which pip3
/usr/local/python/bin/pip3
#
# pip3 install --upgrade pip

Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Requirement already satisfied: pip in /usr/local/python/lib/python3.12/site-packages (24.0)
Collecting pip
Downloading https://www.piwheels.org/simple/pip/pip-24.1.2-py3-none-any.whl (1.8 MB)
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq 1.8/1.8 MB 941.2 kB/s eta 0:00:00
Installing collected packages: pip
Attempting uninstall: pip
Found existing installation: pip 24.0
Uninstalling pip-24.0:
Successfully uninstalled pip-24.0
Successfully installed pip-24.1.2
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#

pip と pip3 は同じものみたいです。
$ pip3 --version
pip 24.1.2 from /usr/local/python/lib/python3.12/site-packages/pip (python 3.12)
$
$ pip --version
pip 24.1.2 from /usr/local/python/lib/python3.12/site-packages/pip (python 3.12)
$

というわけで、Python 3.12.4 のインストールは一応終わりました。Python3 でデータベースを使う時は、 gdbm か sqlite3 を押しているみたいですね。

これで、Python3 3.12.4 の Raspberry Pi へのインストールは完了です。

こちらでは、Python を無料で学習するのが目的なので、そろそろ終わる SimplePrograms に続いて何かないかと、
https://www.python.org/
から探していたら、次のものを見つけました。

PyFlo
https://pyflo.net/
The beginners guide to becoming a Python programmer


なので、機会があればこちらも並行して見てみたいと思います。 そこでは、 「Mu エディタ」 を推薦していますが、こちらは Raspberry Pi を使って Python 3.12.4 で色々試すのは従来通りです。

今回の作業によって、Raspberry Pi をディスプレイとか繋がずに単体で立ち上げて、ネットワークを通して ssh でログインして使うと、プログラミング学習に有効であることが分かりました。しかも、最新版の Python がこのように使えます。小さいので安定感を心配する方もいるかもしれませんが、こちらでは電源入れっぱなしで Webサーバとかも起動してあるのですが、数年間動いています。

プログラミング言語学習のためにスクールに行くとしても、家に実行環境がないと苦労するような気がするのですが、そこらへんはどうなっているのかが気になります。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ + リスト内包表記

2024-07-19 22:01:26 | Python
SimplePrograms 18 lines のプログラムの中にある、
return 文が返すものを「リスト内包表記(List Comprehensions)」と言うのですね。

    return [solution+[(n,i+1)]
       for i in range(BOARD_SIZE)
          for solution in smaller_solutions
             if not under_attack(i+1, solution)]


Python のチュートリアルに使い方が書いてあります。
https://docs.python.org/ja/3/tutorial/datastructures.html#list-comprehensions
----------
squares = [x**2 for x in range(10)]

これはより簡潔で読みやすいです。

リスト内包表記は、括弧の中の 式、 for 句、そして0個以上の for か if 句で構成されます。 リスト内包表記の実行結果は、 for と if 句のコンテキスト中で式を評価した結果からなる新しいリストです。
----------


このリスト内包表記の例は、これと同じです。

----------
>>> squares = []
>>> for x in range(10):
... squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
----------


ここで、append() は、次のようなリストデータ型のメソッドですね。
https://docs.python.org/ja/3/tutorial/datastructures.html#more-on-lists

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


つまり、インストールしたばかりの Python 3.12.4 で試すと、
>>> s = []
>>> s
[]
>>> s.append(2)
>>> s
[2]
>>> s.append(4)
>>> s
[2, 4]
>>>
>>> len(s)
2
>>> s[len(s):] = [6]
>>> s
[2, 4, 6]
>>>
ですね。

と言うことで、18 lines のリスト内包表記は、

括弧の中の式: solution+[(n,i+1)]
for 句: for i in range(BCARD_SIZE)
そして、1個の for 句 : for solution in smaller_solutions
その中の if 句 : if not under_attack(i+1, solution)


という構造ですね。これは、先ほどの tutorial の

---------
例えば、次のリスト内包表記は2つのリストの要素から、違うもの同士をペアにします。
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

これは次のコードと等価です:
>>> combs = []
>>> for x in [1,2,3]:
... for y in [3,1,4]:
... if x != y:
... combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

for と if 文が両方のコードで同じ順序になっていることに注目してください。
----------

の例と一緒ですね。

ちなみに、
>>> vec = [-4 , -2, 0, 2, 4]
>>> [x*2 for x in vec ]
[-8, -4, 0, 4, 8]
>>> x
Traceback (most recent call last):
File "<stdin&gh;", line 1, in <module>
NameError: name 'x' is not defined
>>>
なので、リスト内包表記内で生成された変数は外では使えないようです。

また、リスト内包表記をGemini に聞いたら、

ここから--------------
メリット
* forループと条件分岐を一行で記述できるため、コードが簡潔になる
* 読みやすく、処理内容が直感的に理解しやすい
* 効率的にリストを作成できる
--------------ここまで

とメリットを上げていて、まとめとして、

ここから--------------
リスト内包表記は、Python初心者にとって最初は少し難しく感じるかもしれませんが、慣れてくると非常に便利で強力なツールになります。
--------------ここまで

と言っているので、積極的に使った方が良さそうです。SimplePrograms に使ったのも、そんな意図がありそうで、Python 入門者もこれを使って慣れておきたいと思いました。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Raspberry Pi に最新の Python3 をインストールする

2024-07-14 08:32:34 | Python
連休なのでちょっと寄り道して、今使っている Raspberry Pi に、最新の Python3 をインストールしてみました。

使っている OS は、

$ cat /etc/os-release
PRETTY_NAME="Raspbian GNU/Linux 12 (bookworm)"
NAME="Raspbian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"
$

です。

https://www.python.org/
から、最新の Python を調べると、現時点では、 3.12.4 でした。

早速、インストールしてみました。終わったら、 /usr/local/python の下にインストールすることにします。
$ cd
$ wget https://www.python.org/ftp/python/3.12.4/Python-3.12.4.tgz
$ tar xvf Python-3.12.4.tgz
$ cd Python-3.12.4
$ ./configure --prefix=/usr/local/python --enable-optimizations
$ make


(このマシンですと50分から1時間ぐらいかかりました。
 以下、途中で気になった make 時の出力を示します。 .... は途中省略を表します。
 例によって、空白文字が正しく表示されていません。)

......

The following modules are *disabled* in configure script:
_sqlite3

The necessary bits to build these optional modules were not found:
_bz2 _ctypes _ctypes_test
_curses _curses_panel _dbm
_gdbm _hashlib _lzma
_ssl _tkinter _uuid
readline
To find the necessary bits, look in configure.ac and config.log.

Could not build the ssl module!
Python requires a OpenSSL 1.1.1 or newer

(これらのソフトは、必要に応じてあらかじめインストールしておいた方がよいようです。
 特に OpenSSL 1.1.1 以上が必要みたいですが、
 Python3 で SSL は、どのように使うのか興味深いですね。)

.......

# Next, run the profile task to generate the profile information.
./python -m test --pgo --timeout=1200 || true
Using random seed: 1449334811
0:00:00 load avg: 1.00 Run 44 tests sequentially (timeout: 20 min)
0:00:00 load avg: 1.00 [ 1/44] test_array
0:00:08 load avg: 1.00 [ 2/44] test_base64
0:00:11 load avg: 1.00 [ 3/44] test_binascii
0:00:11 load avg: 1.00 [ 4/44] test_binop
0:00:12 load avg: 1.00 [ 5/44] test_bisect
0:00:13 load avg: 1.00 [ 6/44] test_bytes
0:00:50 load avg: 1.00 [ 7/44] test_bz2 -- test_bytes passed in 37.6 sec
0:00:51 load avg: 1.00 [ 8/44] test_cmath -- test_bz2 skipped
0:00:52 load avg: 1.00 [ 9/44] test_codecs
0:01:02 load avg: 1.00 [10/44] test_collections
0:01:10 load avg: 1.00 [11/44] test_complex
0:01:12 load avg: 1.00 [12/44] test_dataclasses
0:01:16 load avg: 1.00 [13/44] test_datetime
0:01:40 load avg: 0.94 [14/44] test_decimal
0:02:44 load avg: 0.98 [15/44] test_difflib -- test_decimal passed in 1 min 3 sec
0:02:52 load avg: 0.98 [16/44] test_embed
0:03:38 load avg: 0.99 [17/44] test_float -- test_embed passed in 45.5 sec
0:03:40 load avg: 0.99 [18/44] test_fstring
0:04:01 load avg: 1.00 [19/44] test_functools
0:04:06 load avg: 1.00 [20/44] test_generators
0:04:09 load avg: 1.00 [21/44] test_hashlib
0:04:12 load avg: 1.00 [22/44] test_heapq
0:04:18 load avg: 1.00 [23/44] test_int
0:04:21 load avg: 1.00 [24/44] test_itertools
0:05:04 load avg: 1.00 [25/44] test_json -- test_itertools passed in 42.2 sec
0:09:35 load avg: 1.00 [26/44] test_long -- test_json passed in 4 min 31 sec
0:10:16 load avg: 1.00 [27/44] test_lzma -- test_long passed in 41.5 sec
0:10:17 load avg: 1.00 [28/44] test_math -- test_lzma skipped
0:10:44 load avg: 1.00 [29/44] test_memoryview
0:10:48 load avg: 1.00 [30/44] test_operator
0:10:49 load avg: 1.00 [31/44] test_ordered_dict
0:10:58 load avg: 1.00 [32/44] test_patma
0:10:59 load avg: 1.00 [33/44] test_pickle
0:11:56 load avg: 1.00 [34/44] test_pprint -- test_pickle passed in 56.6 sec
0:11:58 load avg: 1.00 [35/44] test_re
0:12:05 load avg: 1.00 [36/44] test_set
0:12:46 load avg: 1.00 [37/44] test_sqlite3 -- test_set passed in 40.7 sec
0:12:47 load avg: 1.00 [38/44] test_statistics -- test_sqlite3 skipped
0:13:05 load avg: 1.00 [39/44] test_struct
0:13:11 load avg: 1.00 [40/44] test_tabnanny
0:13:14 load avg: 1.00 [41/44] test_time
0:13:17 load avg: 0.92 [42/44] test_unicode
0:13:37 load avg: 0.94 [43/44] test_xml_etree
0:13:41 load avg: 0.95 [44/44] test_xml_etree_c

Total duration: 13 min 49 sec
Total tests: run=8,314 skipped=175
Total test files: run=44/44 skipped=3
Result: SUCCESS
( Python3 は、make 時にテストをするみたいです。
 make check とかしたいところですが、時間がかかりそうなのでこれで良しとします。)
$

make できたので、さっそくインストールします。

$ su
# mkdir /usr/local/python
# make install
# exit

インストールできたか確認してみます。

$ ls /usr/local/python
bin include lib share
$
$ ls /usr/local/python/bin
2to3 idle3 pip3 pydoc3 python3 python3.12
2to3-3.12 idle3.12 pip3.12 pydoc3.12 python3-config python3.12-config
$
$ ls /usr/local/python/lib
libpython3.12.a pkgconfig python3.12
$


OpenSSL とか使えないので、今回インストールした Python3 は標準では使えないようにしているので、
環境変数 PATH と LD_LIBRARY_PATH を再設定します。

$ export PATH=/usr/local/python/bin:$PATH
$ export LD_LIBRARY_PATH=/usr/local/python/lib

実行してみます。

$ python3
Python 3.12.4 (main, Jul 13 2024, 23:01:23) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>


ちゃんと、3.12.4 になっています。
Raspberry Pi でも最新の Python3 が一応使えるので、Raspberry Pi の有効性があらためて確認できました。

追記:使用した Raspberry Pi はちょっと古い、
$ cat /proc/cpuinfo
.............
Model : Raspberry Pi 4 Model B Rev 1.2
$
です。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ + 代入文について

2024-07-09 22:27:49 | Python
SimplePrograms の 17番目のプログラムで、気になる代入の形が出てきたので少し調べてみました。

Python の場合、代入は「代入文 (Assignmet statement) 」で規定されているようです。
https://docs.python.org/ja/3/reference/simple_stmts.html#assignment-statements

「文」だとすると

left = right = col

は出来ないと思えます。実際 JavaScript などは、代入は「式」となっています。

そこで、もう一度定義をじっくり見てみました。(インデントがいい加減ですのでご容赦ください。)

assignment_stmt ::= (target_list "=")+ (starred_expression | yield_expression)
target_list    ::= target ("," target)* [","]
target        ::= identifier
          | "(" [target_list] ")"
          | "[" [target_list] "]"
          | attributeref
          | subscription
          | slicing
          | "*" target

starred_expression ::= expression | (starred_item ",")* [starred_item]
starred_item ::= assignment_expression | "*" or_expr


ちゃんと見てみたら、assignment_stmt に定義されていました。
(target_list "=")+
なので、
target_list1 = target_list2 = .....
となって、代入文として連鎖代入ができるのですね。

さらに、target_list が、
target ("," target)* [","]
となっているので、
target1 , target2 , target3 .... = .......
が可能なので、

left, right = left - 1, right + 1

も出来るのですね。これは 4 lines プログラムでも出てきたのですが、右側がタプルだったのでそんなものかと勘違いしていました。

改めて、試して見ましょう。
連鎖代入は、
>>> a = b = c = 4
>>> print(a, b, c)
4 4 4


target が複数の場合は、
>>> a , b = 5 , 6
>>> print(a, b, c)
5 6 4
>>>


target は、丸括弧や、角括弧で囲めるので、
>>> (a , b ) = 7, 8
>>> print(a,b,c)
7 8 4
>>> [a , b ] = 9, 10
>>> print(a,b,c)
9 10 4
>>>


更に、定義どおり最後に "," があっても大丈夫です。
>>> a, b, c, = 10, 20, 30
>>> print(a,b,c)
10 20 30
>>> a, b, c, = 100, 200, 300,
>>> print(a,b,c)
100 200 300
>>>


target_list は、左から右へ順番に代入されるようで、target が添字表記(subscription) の時、良くわかります。
>>> print(d)
[1, 2, 3, 4, 5]
>>> i, d[ i ] = 2 , 30
>>> print(d)
[1, 2, 30, 4, 5]
>>>


"=" の右側は、4 lines プログラムのようにタプルでも良いので、リストも良いみたいです。
>>> a , b, c = 1, 2, 3
>>> a, b = (10, 20)
>>> print(a,b,c)
10 20 3
>>> a, b = [100, 200]
>>> print(a,b,c)
100 200 3

この代入するタプルやリストは、予め変数に代入しておくことが可能です。
>>> print(a, b, c)
100 200 3
>>> d = [1, 2]
>>> print(d)
[1, 2]
>>> a, b = d
>>> print(a, b, c)
1 2 3
>>>

ついでに、target がスライス表記(slicing) の場合を見てみます。
>>> d = [1, 2, 3, 4, 5, 6]
>>> d[1:3]
[2, 3]
>>> d[2:4]
[3, 4]
>>> d[2:4] = 30, 40
>>> print(d)
[1, 2, 30, 40, 5, 6]
>>> d[0:2]
[1, 2]
>>>

通常は、「最初の添字:最後の添字」となりそうですが、Python の場合は違いますね。
これは、文字列のインデックス表記と同じで、
https://docs.python.org/ja/3/tutorial/introduction.html#text
から引用すると、

----------
インデクス表記に加え、 スライス もサポートされています。インデクス表記は個々の文字を取得するのに使いますが、 スライス を使うと部分文字列を取得することができます:
>>> word = 'Python' # これは追記しました。
>>> word[0:2] # characters from position 0 (included) to 2 (excluded)
'Py'
>>> word[2:5] # characters from position 2 (included) to 5 (excluded)
'tho'

スライスの使い方をおぼえる良い方法は、インデックスが文字と文字の あいだ (between) を指しており、最初の文字の左端が 0 になっていると考えることです。そうすると、 n 文字からなる文字列中の最後の文字の右端はインデックス n となります。例えばこうです:

+---+---+---+---+---+---+
| P  |  y  |  t  |  h |  o |  n |
+---+---+---+---+---+---+
0    1     2     3    4    5    6
-----------

つまり、インデクスは要素の間を表し、この図で言えば、
[a:b]なら[インデクスの右側の文字:インデクスの左側の文字]なので、リストなら、
a の右側の要素から、b の手前の要素までを表すようです。

そのため、d[2:4] なら、先頭要素を0番目として、2番目の要素から3番目の要素までを表すのですね。

定義の最後によると、 target の頭に "*" を付けることができるようです。しかし、

>>> *c = [1, 2, 3]
File "<stdin>", line 1
SyntaxError: starred assignment target must be in a list or tuple
>>>


なので、色々試したことろ、次のようにできることが分かりました。

>>> a, b, *c = [1, 2, 3, 4, 5, 6]
>>> print(a, b, c)
1 2 [3, 4, 5, 6]
>>>
>>> a, b, *c = 1, 2, 3, 4, 5
>>> print(a, b, c)
1 2 [3, 4, 5]
>>>
>>> a, b, *c = (1, 2, 3, 4, 5)
>>> print(a, b, c)
1 2 [3, 4, 5]
>>>


興味深いです。ちなみに、代入文で詳しそうなのを探したら、以下のものを見つけました。

Python's Assignment Operator: Write Robust Assignments
https://realpython.com/python-assignment-operator/

(中学生や高校生の方は、英語なので昔だったら諦めるところですが、現在はブラウザで翻訳できるので敷居が低くなりました。)

これからも分かるように、Python の「代入」は奥が深そうですね。

今回は、「代入文」だけになりました。次回は「リスト内包表記」に触れてみたいと思います。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SimplePrograms で Python を学ぶ その17

2024-07-07 09:44:30 | Python
SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms

の 17番目のプログラムは、 18行の再帰呼び出し(エイト・クイーン問題)です。

BOARD_SIZE = 8

def under_attack(col, queens):
    left = right = col

    for r, c in reversed(queens):
       left, right = left - 1, right + 1

       if c in (left, col, right):
          return True
    return False

def solve(n):
    if n == 0:
       return [[]]

    smaller_solutions = solve(n - 1)

    return [solution+[(n,i+1)]
       for i in range(BOARD_SIZE)
          for solution in smaller_solutions
             if not under_attack(i+1, solution)]
for answer in solve(BOARD_SIZE):
    print (answer)



ファイルにして実行してみます。出力は92行(解の総数)あるのでほとんど省略します。

$ ./prog-18.py
[(1, 4), (2, 2), (3, 7), (4, 3), (5, 6), (6, 8), (7, 5), (8, 1)]
.......... 省略 ....................
[(1, 4), (2, 7), (3, 5), (4, 2), (5, 6), (6, 1), (7, 3), (8, 8)]
[(1, 5), (2, 7), (3, 2), (4, 6), (5, 3), (6, 1), (7, 4), (8, 8)]
$

17番目のプログラムなのに18行で、これ以降のサンプルプログラムは行が飛び飛びになっており、これまでよりより具体的なプログラムになっているようです。今後どうするかは、考えてみます。

これは、だいぶ前に Microsoft の Bing に質問した解説があるので紹介します。現在どうなっているかは試して見てください。
まずは、最初の8行の部分です。
ここから ----------------------
こんにちは、これはBingです。??

8-queens の問題とは、8×8のチェス盤に8つのクイーンを互いに攻撃しないように配置する問題です12。一般に、n-queens の問題として、n×nのチェス盤にn個のクイーンを配置する問題も考えられます1。

Python で書かれたサンプルプログラムは、再帰的な関数とリスト内包表記を使って、n-queens の問題のすべての解を求めるものです3。コードの各部分について説明します。

(1)まず、BOARD_SIZE = 8 という行は、チェス盤のサイズを定義しています。この値を変えると、n-queens の問題に対応できます。

(2)次に、under_attack(col, queens) という関数は、ある列番号 col と既に配置されたクイーンのリスト queens を引数にとり、その列にクイーンを置くことができるかどうかを判定する関数です。この関数では、以下の処理を行っています。

(3)・ left と right という変数に col を代入します。これらは、左斜め上方向と右斜め上方向にあるクイーンの列番号を表します。
(4)・ for r, c in reversed(queens): というループで、queens のリストを逆順に取り出しています。r は行番号、c は列番号です。
(5)・ left, right = left - 1, right + 1 という行で、left と right をそれぞれ1ずつ減らし、増やしています。これは、斜め上方向に移動することを表しています。
(6)・ if c in (left, col, right): という条件文で、c が left, col, right のいずれかと等しいかどうかを判定しています。これは、同じ列か同じ斜め方向にクイーンがあるかどうかを判定しています。
(7)・ もし条件が真ならば、True を返します。これは、その列にクイーンを置くことができないことを表しています。
(8)・ もしループが終了したら、False を返します。これは、その列にクイーンを置くことができることを表しています。

この関数は、クイーンの配置が可能かどうかを高速に判定するためのものです。
---------------------- ここまで (対応する行を () で追記してあります。

次に solve() の部分(9行目から16行目まで)です。

ここから ----------------------
( 9)次に、solve(n) という関数は、n 個のクイーンを配置する問題のすべての解を求める関数です。この関数では、以下の処理を行っています。

(10)・ if n == 0: という条件文で、n が0かどうかを判定しています。これは、すべてのクイーンが配置されたかどうかを判定しています。
(11)・ もし n == 0 ならば、[[]] を返します。これは、空の解のリストを表しています。

(12)・ もし n != 0 ならば、smaller_solutions = solve(n - 1) という行で、smaller_solutions という変数に、n-1 個のクイーンを配置する問題のすべての解のリストを代入しています。これは、再帰的に関数を呼び出すことで、問題を小さく分割して解いていることを表しています。

(13)・ return [solution+[(n,i+1)] for i in range(BOARD_SIZE) for solution in small_solutions if not under_attack(i+1, solution)] という行(つまり、13,14,15,16行)で、リスト内包表記を使って、
n 個目のクイーンを配置する可能性のあるすべての解を生成しています。この行では、以下の処理を行っています。

(14) ・ for i in range(BOARD_SIZE): というループで、i を0から7まで変化させています。これは、列番号を表しています。
(15) ・ for solution in smaller_solutions: というループで、smaller_solutions のリストから要素を取り出しています。これは、n-1 個のクイーンを配置する問題の解を表しています。
(16) ・ if not under_attack(i+1, solution): という条件文で、i+1 番目の列にクイーンを置くことができるかどうかを判定しています。これは、under_attack 関数を呼び出して、クイーンが互いに攻撃しないかどうかを判定しています。

・ もし条件が真ならば、solution+[(n,i+1)] をリストに追加します。これは、n 個目のクイーンを i+1 番目の列に置いた場合の解を表しています。

・ このようにして、n 個のクイーンを配置する問題のすべての解のリストを返します。
---------------------- ここまで (対応する行を () で追記してあります。

最後に答えを出力する部分です。
ここから ----------------------
(17)(18)最後に、for answer in solve(BOARD_SIZE): print (answer) という行で、solve(BOARD_SIZE) 関数を呼び出して、8-queens の問題のすべての解を求めています。そして、for ループでそれらの解を表示しています。

このプログラムは、再帰的な関数とリスト内包表記というPython の特徴的な機能を使って、簡潔に書かれています。しかし、初心者にとっては難しいかもしれません。もっと詳しく説明が必要な場合は、教えてください。
---------------------- ここまで (対応する行を () で追記してあります。

もっともらしく解説文を作ってくれました。便利ですね。

このプログラムから、Python は再帰呼び出しができる言語ということが分かりました。

(4)行目の reversed() は組み込み関数で、引数を逆順に取り出すイテレータを返すもののようです。

>>> b
[(1, 2), (3, 4), (5, 6), (7, 8)]
>>> reversed(b)
<list_reverseiterator object at 0xf798f730>
>>>
>>> for i,j in reversed(b):
...        print(i,j)
...
7 8
5 6
3 4
1 2
>>>

(6)行目の if 文の中の in は、
in の後ろのタプルの中に含まれるかどうかの判定に使うのですね。ここは、Copilot に聞いてみました。

ここから ----------------------
`in` は、リストや辞書の中に特定の値が含まれているかどうかを判定するために使います。
(途中省略)
要するに、`in` は特定の値がコンテナ(リストや辞書など)に含まれているかどうかを確認するのに便利なキーワードです。
---------------------- ここまで

タプルが使えるかどうかを確認すると、

ここから ----------------------
はい、`if` 文の中で `in`を使う際に、リストの代わりにタプルを使用することもできます。
タプルはリストと似ていますが、要素を変更できない不変のコレクションです。
---------------------- ここまで

Python のプログラムで分からない所の確認に生成AIは有効ですね。例も出てくるので実際に試せますから。
ためしてみると、次のようになりました。

>> print(a)
(1, 2, 3)
>>> print(b)
(4, 5, 6)
>>> print(c)
2
>>> if c in a:
...        print('True')
...    else:
...        print('False')
...
True
>>> if c in b:
...        print('True')
...    else:
...        print('False')
...
False
>>>
タプルでなくてリストでも大丈夫です。

>>> a = [1, 2, 3]
>>> if c in a:
...        print('True')
...    else:
...        print('False')
...
True
>>>

これは、よく使われそうです。

ここで、気になったのが
(3)行目のいわゆる連鎖代入
(5)行目のいっぺんに2つの変数に代入できる?表記と、
(13)~(15) の return にある、「リスト内包表記」です。

長くなったので、次回はこれに付いて調べて見たいと思います。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする