🐷

Pythonが教育用途において十分だという話

に公開
6

Discussion

ruby_funruby_fun

「プログラミングの学習向け」という目的に必要なことが、Pythonで本当に簡単にできますか?

プログラミング言語を使った基礎的な書き方(文法や慣用句)を覚えたら、その次は、
データサイエンス分野でもWebアプリ分野でも共通的なアルゴリズムを学ぶための前提的な「高校までの数学の計算演習問題」を簡単なコードで計算できるという”体験”が重要でしょう。

まず、「アルゴリズムの学習って、何が嬉しいの?」という、動機付け;

# フィボナッチ数列の第n項の計算量が、(n)のオーダより早い(log(n))を体験できるコード;
require 'matrix'
p "(6) フィボナッチ数列ランダムアクセス"
[2,4,5,6,7,8,9,16,17,32,33,64,65,255,256,257].each {|n|
f=(Matrix[[0,1],[1,1]] ** (n))[0,0]
p [n,f.to_s().length, f]
}
#==> [2, 1, 1]
#==> [4, 1, 2] 中略
#==> [256, 53, 87571595343018854458033386304178158174356588264390370]
#==> [257, 54, 141693817714056513234709965875411919657707794958199867]

このRubyコードの例の場合、べき乗演算子(**)の指数部の引数が整数の場合に特化した高速アルゴリズムが存在していることや、高校の数学の計算問題としては簡単な「行列のべき乗」というアルゴリズムで、「フィボナッチ数列のランダムアクセスができる」という、アルゴリズムの有難み体験できます。

次に 高校までの数学の計算問題;

--# 多倍長整数係数の複素数の分数計算;
p "(1)",x=Rational(12345678901234567890 ,(1 - 12345678901234567891i)) -
Rational(12345678901234567891,(1 + 12345678901234567890i))
#==> ((-228623681298582551271376318164380429986
/ 11615286144559076666048468336368822614758804202921592629334943372565917420041) +
(23230572289118153332096936672737645229441400512076991074912761305743708030085 /
11615286144559076666048468336368822614758804202921592629334943372565917420041)*i)

この手計算ではヤリタクナイ計算は、Ruby言語のgenericな標準ライブラリの動的型付け機能によって、
Rational<Complex<BicInteger>>な式の四則演算や通分の過程で、Complex<Rational<BicInteger>>などの型に動的に型変換されて計算結果が得られています。
Pythonの分数classは、静的に数値型を継承したclassしか引数にとれなくて、動的に発生するComplex<BicInteger>のような型のインスタンスを保持できない、静的型付けされたライブラリ体系なので、標準ライブラリだけの1文では計算できないし、拡張ライブラリであるsympyをもってしても、通分が不完全だったりして、「高校までの数学の計算演習問題」を簡単なコードで計算できるという、体験ができないでしょう。

"再利用性のある自作ライブラリ"の体験;

さらに、次に学習で体験すべきは、
「1回だけ定義した関数に、色々な型を適用して、使いまわしできる」という、
「自作ライブラリの再利用性」の体験でしょう。

// -- # 一次方程式の解の公式"
def SolvingLinearEquations(y,a,b)
x= (y -b) / a
end

p "実数解",SolvingLinearEquations(1.0,5, 0.5)
p "分数解",SolvingLinearEquations(Rational(1,1), Rational(5,1), Rational(1,2))
p "虚数解",SolvingLinearEquations(1+1i, 5, 1.0 /(2+ 2i))
p "虚数&分数解",SolvingLinearEquations(Rational(1+1i, 1), Rational(5,1), Rational(1,2+ 2i))
p "多倍長整数の行列解",SolvingLinearEquations(Matrix[[Rational(1234567890123456789890,1),Rational(0,1)]],
Matrix[ [Rational(1234567890123456789890,1), Rational(12345678901234567898902,1)],
[Rational(1234567890123456789890,1), Rational(1234567890123456789890
3,1)] ],
Matrix[[Rational(1234567890,1),Rational(123456789,1)]] )
p "多倍長整数 複素数分数の行列解",SolvingLinearEquations(Matrix[[Rational(1234567890123456789890,1i),Rational(0,1)]],
Matrix[ [Rational(1234567890123456789890,1), Rational(12345678901234567898902,1i)],
[Rational(1234567890123456789890,1), Rational(1234567890123456789890
3,1i)] ],
Matrix[[1234567890, 0+1i]] )

/ /-- # h t t p s : //ideone.com/cJ1hIF

ところが、Pythonの場合、四則演算子が、スカラー型のclassと、行列系クラス(リストのリストに非ず)とで、異なるメソッド名が付与されているので、それらの種類ごとにSolvingLinearEquationsを 何度も定義しなくてはダメで、中途半端に「再利用性の無いライブラリ」の例を体験できるだけでしょう。

学習者向け 演習環境----------------

上述の「高校までの数学の計算演習問題」を教材にした体験学習を、決して暴走しない、ideoneのような、オンラインの演習環境で、標準ライブラリだけで、試せることが、学習向け教材としては必要でしょう。
ライブラリのバージョン管理とか、操作ミスを誘発する作業は、「高校までの数学の計算演習問題」を教材にした体験より、後で良いでしょう。
この点、Pythonだと、Pythonの利用分野でポピュラーな、numPyやsympyなどがideone.comでは動作しないので、学習者向けオンライン演習環境を探すことも面倒でしょう。

sigmasigma

回答になっているか分からないですがcolaboratoryを使えばいいのではないでしょうか。

以下utokyo-ippさんが公開しているjupyternotebookです。
https://colab.research.google.com/github/utokyo-ipp/utokyo-ipp.github.io/blob/master/colab/5/5-3.ipynb

ruby_funruby_fun

えーと、「学習者向け 演習環境として、jupyter notebookが使える、クラウド上のColaboratory を使えば良い」というのは、先の提言への回答の ひとつですね。

「Pythonが教育用途において”十分”だ」と言われたからには、
「プログラミングの学習向けの教材としてのアラユル要件を満たす」と主張したに等しく、
先に示した「プログラミングの学習向け教材としての4要件 程度の”基礎的”なことは、
 Pythonが全て満たす」という、
コースウェア開発に貢献しうるPythonの機能や仕様が”十分に揃っている”ことも、示されるのかと思います。

先に例示したコードは 各々数行で、
「初学者の1週間目は、1行のコードが書けて、2週間目なら3行まで書けて、3週間目に10行まで書けて、4週間目なら30行、5週間目なら百行書ける」という習熟度だとしても。
3週間以内に 先の演習コードを体験できるでしょう。

でも、Python言語で、同等出力ができるコードを書くのには、数百行要すので、3週間以内には、先の演習課題を解けないでしょう。

解答コードがタッタの1行のコードなのに、「難しい」とか「解らない」という人が居るという、証拠になるようなコメントも付いていますよね。

例えば、高校までの数学の計算問題の”多倍長整数 複素数分数の行列”解として、
Matrix[[((-3703703671/1234567890123456789890)-(3/1)*i),
  ((2469135781/1234567890123456789890)+(2/1)*i)]]

というのは、正解になるし、ついでに、それを浮動小数点数で表現した
「Matrix[[[-3.0000000005100002e-12, -3.0], [2.00000000061e-12, 2.0]]]」
というのも 添えるのも、okでしょうが、
Pythonのnumpyを使って後者しか計算できないのは、不正解となるでしょう。

Pythonのnumpyライブラリの場合は、浮動小数点ベースの演算に 型が拘束された 静的型付け的なライブラリなので、先に例示した、多様な型の計算ができないということが、再利用性の無さの根源です。

もし、Pythonの標準ライブラリにgenericな多様性を持たせて ライブラリ体系を再設計すれば、Ruby並みの簡素なコードを書くことができるようになるかもしれません。
でも、それでは、Pythonの互換性が損なわれてしまうので、もはや「高校までの数学の計算問題で、アルゴリズムや再利用性を体験する」という使い道には、手遅れなのです。

ご異論が在るなら、まずは、先に例示したRubyコードと 同等な出力ができるPythonコードを、各々数行、合計百行未満で、書いて見せていただきたいものです。

5週間以上Pythonを学習された方なら、百行未満のコードは書けるでしょうが、私の想定である「Ruby言語で百行未満の演習課題と同等コードは、Python言語だと8百行程度になる」となれば、易々と、Pythonコードを提示できる人は出ては来ないでしょう。

白鳥白鳥

まず前提をすり合わせることが建設的な議論をするために重要だと思います。

なのでこの場合「プログラミングの学習向け教材の要件」をすり合わせることが先決です。

ruby_funさんのコメントはその大部分がruby_funさんの考えた前提に基づくものであり、つまり大部分が現状記事に関係ない話です。

相手の主張を無視してご自身の主張をなさる行為は一般的にリスペクトに欠けているとみなされる行為であり、著者の方の主張を羊頭狗肉と侮蔑的な表現をすることは一般的に人を不快にさせる行為です。

つまりは現状ruby_funさんはzennのコミュニティガイドラインに違反し、利用規約の4条12項にも違反していると思われます。

ruby_funさんはそういった行為をやめて建設的な議論を目指された方がいいと私は思いました。

PS.
記事の内容は主に保守性の高いコードを書くためにどの言語でも利用される概念をベースとしたライブラリの紹介で私はとても素晴らしいと思いました。

タコタコ

frozenによりデータがイミュータブルであることを保証しています。

frozenはイミュータブルではありません。リフレクションによってチェックするだけです。

>>> user.__dict__['name'] = 'invalid'
>>> user
User(name='invalid', email='sigma@mail.com')

また、frozenは表層的な代入しか検査しません。frozenクラスのインスタンスの中に別のオブジェクトのインスタンスある場合に、中のインスタンスを書き換えることができます。

>>> user.profile.email = 'invalid' # 可能

Pythonではこれらを禁止することができません。

Pythonで書かれた酷いコードを減らすためにPythonを滅ぼしても他の言語で酷いコードを書くだけなんじゃないですかね。どう考えてもその層はRustとかには流れない。

私は長年Pythonを書いてきましたが、深層学習が流行りだしたあたりから明らかに酷いコードを書く人が増えたという印象を持っています。そして彼らの多くがPythonしか書けません。
初学者にいきなりRustを勉強させるのは酷だと思いますが、ある程度経験を積んだ人には使うか使わないかに関係なく、酷いコードをコンパイルエラーにしてくれるRustを勉強してほしいという気持ちはあります。

sigmasigma

また、frozenは表層的な代入しか検査しません。frozenクラスのインスタンスの中に別のオブジェクトのインスタンスある場合に、中のインスタンスを書き換えることができます。

これについてはprofileをdataclassで書いてfrozenにすることで防げると思います。

@dataclass(frozen=True)
class Profile:
    name: str
    email: str

@dataclass(frozen=True)
class User:
    profile: Profile

user = User(
    Profile('sigma',  'sigma@email.com')
)

user.profile.name = 'invalid' #error

言語機能としてイミュータブルを強制することができないのは自分も酷いなと思っています。いわゆる「みんないい大人なんだから(We're all consenting adults here)」というモットーで正当化されている部分です。

とはいえ何故Pythonにちゃんとしたイミュータブルなクラスが無いのかという説明をイミュータブルという概念を獲得する以前にすることが難しいため、ここではイミュータブルと呼んでいます。注釈でも加えておきます。